Skip to content

Commit

Permalink
refactor: methods for selecting sub tree and equality of category trees
Browse files Browse the repository at this point in the history
  • Loading branch information
dhhyi committed Jun 18, 2020
1 parent 6aef75e commit 4c422aa
Show file tree
Hide file tree
Showing 2 changed files with 234 additions and 0 deletions.
185 changes: 185 additions & 0 deletions src/app/core/models/category-tree/category-tree.helper.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as using from 'jasmine-data-provider';
import { CategoryData } from 'ish-core/models/category/category.interface';
import { CategoryMapper } from 'ish-core/models/category/category.mapper';
import { Category } from 'ish-core/models/category/category.model';
import { categoryTree } from 'ish-core/utils/dev/test-data-utils';

import { CategoryTreeHelper } from './category-tree.helper';
import { CategoryTree } from './category-tree.model';
Expand Down Expand Up @@ -458,4 +459,188 @@ describe('Category Tree Helper', () => {
}
);
});

describe('subTree', () => {
let combined: CategoryTree;

beforeEach(() => {
combined = categoryTree([
{ uniqueId: 'A', categoryPath: ['A'] },
{ uniqueId: 'A.1', categoryPath: ['A', 'A.1'] },
{ uniqueId: 'A.1.a', categoryPath: ['A', 'A.1', 'A.1.a'] },
{ uniqueId: 'A.2', categoryPath: ['A', 'A.2'] },
{ uniqueId: 'A.1.b', categoryPath: ['A', 'A.1', 'A.1.b'] },
{ uniqueId: 'B', categoryPath: ['B'] },
{ uniqueId: 'B.1', categoryPath: ['B', 'B.1'] },
{ uniqueId: 'B.1.a', categoryPath: ['B', 'B.1', 'B.1.a'] },
{ uniqueId: 'B.2', categoryPath: ['B', 'B.2'] },
] as Category[]);
});

it('should be created', () => {
expect(combined).toMatchInlineSnapshot(`
├─ A
│ ├─ A.1
│ │ ├─ A.1.a
│ │ └─ A.1.b
│ └─ A.2
└─ B
├─ B.1
│ └─ B.1.a
└─ B.2
`);

expect(combined.rootIds).toMatchInlineSnapshot(`
Array [
"A",
"B",
]
`);
expect(Object.keys(combined.nodes)).toMatchInlineSnapshot(`
Array [
"A",
"A.1",
"A.1.a",
"A.2",
"A.1.b",
"B",
"B.1",
"B.1.a",
"B.2",
]
`);
expect(Object.keys(combined.edges)).toMatchInlineSnapshot(`
Array [
"A",
"A.1",
"B",
"B.1",
]
`);
});

it('should return an empty tree if selected uniqueId is not part of the tree', () => {
const tree = CategoryTreeHelper.subTree(combined, 'C');
expect(tree.rootIds).toBeEmpty();
expect(tree.edges).toBeEmpty();
expect(tree.nodes).toBeEmpty();
});

it('should return copy of tree if selector is undefined', () => {
const tree = CategoryTreeHelper.subTree(combined, undefined);
expect(CategoryTreeHelper.equals(tree, combined)).toBeTrue();
});

it('should extract sub tree for A', () => {
const tree = CategoryTreeHelper.subTree(combined, 'A');

expect(tree).toMatchInlineSnapshot(`
└─ A
├─ A.1
│ ├─ A.1.a
│ └─ A.1.b
└─ A.2
`);

expect(tree.rootIds).toMatchInlineSnapshot(`
Array [
"A",
]
`);
expect(Object.keys(tree.nodes)).toMatchInlineSnapshot(`
Array [
"A",
"A.1",
"A.1.a",
"A.2",
"A.1.b",
]
`);
expect(Object.keys(tree.edges)).toMatchInlineSnapshot(`
Array [
"A",
"A.1",
]
`);
});

it('should extract sub tree for A.1', () => {
const tree = CategoryTreeHelper.subTree(combined, 'A.1');

expect(tree).toMatchInlineSnapshot(`
└─ dangling
└─ A.1
├─ A.1.a
└─ A.1.b
`);

expect(tree.rootIds).toBeEmpty();
expect(Object.keys(tree.nodes)).toMatchInlineSnapshot(`
Array [
"A.1",
"A.1.a",
"A.1.b",
]
`);
expect(Object.keys(tree.edges)).toMatchInlineSnapshot(`
Array [
"A.1",
]
`);
});

it('should extract sub tree for A.1.a', () => {
const tree = CategoryTreeHelper.subTree(combined, 'A.1.a');

expect(tree).toMatchInlineSnapshot(`
└─ dangling
└─ A.1.a
`);

expect(tree.rootIds).toBeEmpty();
expect(Object.keys(tree.nodes)).toMatchInlineSnapshot(`
Array [
"A.1.a",
]
`);
expect(Object.keys(tree.edges)).toBeEmpty();
});
});

describe('equals', () => {
const catA = { uniqueId: 'A', categoryPath: ['A'] } as Category;
const catB = { uniqueId: 'B', categoryPath: ['B'] } as Category;

it('should return true for simple equal trees', () => {
const tree1 = categoryTree([catA]);
const tree2 = categoryTree([catA]);

expect(CategoryTreeHelper.equals(tree1, tree2)).toBeTrue();
});

it('should return true if category was copied', () => {
const tree1 = categoryTree([catA]);
const tree2 = categoryTree([{ ...catA }]);

expect(CategoryTreeHelper.equals(tree1, tree2)).toBeTrue();
});

it('should return false for simple unequal trees', () => {
const tree1 = categoryTree([catA]);
const tree2 = categoryTree([catB]);

expect(CategoryTreeHelper.equals(tree1, tree2)).toBeFalse();
});

it('should return true for simple unordered trees', () => {
const tree1 = categoryTree([catA, catB]);
const tree2 = categoryTree([catB, catA]);

expect(CategoryTreeHelper.equals(tree1, tree2)).toBeTrue();
});
});
});
49 changes: 49 additions & 0 deletions src/app/core/models/category-tree/category-tree.helper.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { isEqual, pick } from 'lodash-es';

import { Category } from 'ish-core/models/category/category.model';

import { CategoryTree } from './category-tree.model';
Expand Down Expand Up @@ -131,4 +133,51 @@ export class CategoryTreeHelper {
const singleCategoryTree = CategoryTreeHelper.single(category);
return CategoryTreeHelper.merge(tree, singleCategoryTree);
}

/**
* Extract a sub tree.
*/
static subTree(tree: CategoryTree, uniqueId: string): CategoryTree {
if (!uniqueId) {
return tree;
}

const select = (e: string) => e.startsWith(uniqueId);
return {
rootIds: tree.rootIds.filter(select),
edges: pick(tree.edges, ...Object.keys(tree.edges).filter(select)),
nodes: pick(tree.nodes, ...Object.keys(tree.nodes).filter(select)),
};
}

private static rootIdsEqual(t1: string[], t2: string[]) {
return t1.length === t2.length && t1.every(e => t2.includes(e));
}

private static edgesEqual(t1: { [id: string]: string[] }, t2: { [id: string]: string[] }) {
return isEqual(t1, t2);
}

private static categoriesEqual(t1: { [id: string]: Category }, t2: { [id: string]: Category }) {
const keys1 = Object.keys(t1);
const keys2 = Object.keys(t2);
return (
keys1.length === keys2.length &&
keys1.every(id => keys2.includes(id)) &&
keys1.every(id => isEqual(t1[id], t2[id]))
);
}

/**
* Perform check for equality. Order of items is ignored.
*/
static equals(tree1: CategoryTree, tree2: CategoryTree): boolean {
return (
tree1 &&
tree2 &&
CategoryTreeHelper.rootIdsEqual(tree1.rootIds, tree2.rootIds) &&
CategoryTreeHelper.edgesEqual(tree1.edges, tree2.edges) &&
CategoryTreeHelper.categoriesEqual(tree1.nodes, tree2.nodes)
);
}
}

0 comments on commit 4c422aa

Please sign in to comment.