optimized find queries & refactorings
This commit is contained in:
@@ -4,45 +4,50 @@ import { IScript } from './IScript';
|
|||||||
import { IApplication } from './IApplication';
|
import { IApplication } from './IApplication';
|
||||||
|
|
||||||
export class Application implements IApplication {
|
export class Application implements IApplication {
|
||||||
private static mustHaveCategories(categories: ReadonlyArray<ICategory>) {
|
public get totalScripts(): number { return this.flattened.allScripts.length; }
|
||||||
if (!categories || categories.length === 0) {
|
public get totalCategories(): number { return this.flattened.allCategories.length; }
|
||||||
throw new Error('an application must consist of at least one category');
|
|
||||||
|
private readonly flattened: IFlattenedApplication;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public readonly name: string,
|
||||||
|
public readonly version: number,
|
||||||
|
public readonly categories: ReadonlyArray<ICategory>) {
|
||||||
|
if (!name) {
|
||||||
|
throw Error('Application has no name');
|
||||||
|
}
|
||||||
|
if (!version) {
|
||||||
|
throw Error('Version cannot be zero');
|
||||||
|
}
|
||||||
|
this.flattened = flatten(categories);
|
||||||
|
if (this.flattened.allCategories.length === 0) {
|
||||||
|
throw new Error('An application must consist of at least one category');
|
||||||
|
}
|
||||||
|
ensureNoDuplicates(this.flattened.allCategories);
|
||||||
|
ensureNoDuplicates(this.flattened.allScripts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public findCategory(categoryId: number): ICategory | undefined {
|
||||||
|
return this.flattened.allCategories.find((category) => category.id === categoryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public findScript(scriptId: string): IScript | undefined {
|
||||||
|
return this.flattened.allScripts.find((script) => script.id === scriptId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getAllScripts(): IScript[] {
|
||||||
|
return this.flattened.allScripts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function ensureNoDuplicates<TKey>(entities: ReadonlyArray<IEntity<TKey>>) {
|
||||||
* Checks all categories against duplicates, throws exception if it find any duplicates
|
|
||||||
* @return {number} Total unique categories
|
|
||||||
*/
|
|
||||||
/** Checks all categories against duplicates, throws exception if it find any duplicates returns total categories */
|
|
||||||
private static mustNotHaveDuplicatedCategories(categories: ReadonlyArray<ICategory>): number {
|
|
||||||
return Application.ensureNoDuplicateEntities(categories, Application.visitAllCategoriesOnce);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks all scripts against duplicates, throws exception if it find any scripts duplicates total scripts.
|
|
||||||
* @return {number} Total unique scripts
|
|
||||||
*/
|
|
||||||
private static mustNotHaveDuplicatedScripts(categories: ReadonlyArray<ICategory>): number {
|
|
||||||
return Application.ensureNoDuplicateEntities(categories, Application.visitAllScriptsOnce);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks entities against duplicates using a visit function, throws exception if it find any duplicates.
|
|
||||||
* @return {number} Result from the visit function
|
|
||||||
*/
|
|
||||||
private static ensureNoDuplicateEntities<TKey>(
|
|
||||||
categories: ReadonlyArray<ICategory>,
|
|
||||||
visitFunction: (categories: ReadonlyArray<ICategory>,
|
|
||||||
handler: (entity: IEntity<TKey>) => any) => number): number {
|
|
||||||
const totalOccurencesById = new Map<TKey, number>();
|
const totalOccurencesById = new Map<TKey, number>();
|
||||||
const totalVisited = visitFunction(categories,
|
for (const entity of entities) {
|
||||||
(entity) =>
|
totalOccurencesById.set(entity.id, (totalOccurencesById.get(entity.id) || 0) + 1);
|
||||||
totalOccurencesById.set(entity.id,
|
}
|
||||||
(totalOccurencesById.get(entity.id) || 0) + 1));
|
|
||||||
const duplicatedIds = new Array<TKey>();
|
const duplicatedIds = new Array<TKey>();
|
||||||
totalOccurencesById.forEach((count, id) => {
|
totalOccurencesById.forEach((index, id) => {
|
||||||
if (count > 1) {
|
if (index > 1) {
|
||||||
duplicatedIds.push(id);
|
duplicatedIds.push(id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -51,83 +56,37 @@ export class Application implements IApplication {
|
|||||||
throw new Error(
|
throw new Error(
|
||||||
`Duplicate entities are detected with following id(s): ${duplicatedIdsText}`);
|
`Duplicate entities are detected with following id(s): ${duplicatedIdsText}`);
|
||||||
}
|
}
|
||||||
return totalVisited;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runs handler on each category and returns sum of total visited categories
|
interface IFlattenedApplication {
|
||||||
private static visitAllCategoriesOnce(
|
allCategories: ICategory[];
|
||||||
|
allScripts: IScript[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function flattenRecursive(
|
||||||
categories: ReadonlyArray<ICategory>,
|
categories: ReadonlyArray<ICategory>,
|
||||||
handler: (category: ICategory) => any): number {
|
flattened: IFlattenedApplication) {
|
||||||
let total = 0;
|
|
||||||
for (const category of categories) {
|
for (const category of categories) {
|
||||||
handler(category);
|
flattened.allCategories.push(category);
|
||||||
total++;
|
|
||||||
if (category.subCategories && category.subCategories.length > 0) {
|
|
||||||
total += Application.visitAllCategoriesOnce(
|
|
||||||
category.subCategories as ReadonlyArray<ICategory>,
|
|
||||||
handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Runs handler on each script and returns sum of total visited scripts
|
|
||||||
private static visitAllScriptsOnce(
|
|
||||||
categories: ReadonlyArray<ICategory>,
|
|
||||||
handler: (script: IScript) => any): number {
|
|
||||||
let total = 0;
|
|
||||||
Application.visitAllCategoriesOnce(categories,
|
|
||||||
(category) => {
|
|
||||||
if (category.scripts) {
|
if (category.scripts) {
|
||||||
for (const script of category.scripts) {
|
for (const script of category.scripts) {
|
||||||
handler(script);
|
flattened.allScripts.push(script);
|
||||||
total++;
|
}
|
||||||
|
}
|
||||||
|
if (category.subCategories && category.subCategories.length > 0) {
|
||||||
|
flattenRecursive(
|
||||||
|
category.subCategories as ReadonlyArray<ICategory>,
|
||||||
|
flattened);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
return total;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly totalScripts: number;
|
function flatten(
|
||||||
public readonly totalCategories: number;
|
categories: ReadonlyArray<ICategory>): IFlattenedApplication {
|
||||||
|
const flattened: IFlattenedApplication = {
|
||||||
constructor(
|
allCategories: new Array<ICategory>(),
|
||||||
public readonly name: string,
|
allScripts: new Array<IScript>(),
|
||||||
public readonly version: number,
|
};
|
||||||
public readonly categories: ReadonlyArray<ICategory>) {
|
flattenRecursive(categories, flattened);
|
||||||
Application.mustHaveCategories(categories);
|
return flattened;
|
||||||
this.totalCategories = Application.mustNotHaveDuplicatedCategories(categories);
|
|
||||||
this.totalScripts = Application.mustNotHaveDuplicatedScripts(categories);
|
|
||||||
}
|
|
||||||
|
|
||||||
public findCategory(categoryId: number): ICategory | undefined {
|
|
||||||
let result: ICategory | undefined;
|
|
||||||
Application.visitAllCategoriesOnce(this.categories,
|
|
||||||
(category) => {
|
|
||||||
if (category.id === categoryId) {
|
|
||||||
result = category;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public findScript(scriptId: string): IScript | undefined {
|
|
||||||
let result: IScript | undefined;
|
|
||||||
Application.visitAllScriptsOnce(this.categories,
|
|
||||||
(script) => {
|
|
||||||
if (script.id === scriptId) {
|
|
||||||
result = script;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getAllScripts(): IScript[] {
|
|
||||||
const result = new Array<IScript>();
|
|
||||||
Application.visitAllScriptsOnce(this.categories,
|
|
||||||
(script) => {
|
|
||||||
result.push(script);
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user