diff --git a/README.md b/README.md index 24f0863..c81c04c 100644 --- a/README.md +++ b/README.md @@ -122,4 +122,5 @@ Interview Questions - [Task #3](src/interview/create-message-handler) - _Create Message Handler_ - [Task #4](src/interview/create-channel-message-handler) - _Create Channel Message Handler_ - [Task #5](src/interview/create-smart-fetch) - _Create Smart Fetch_ -- [Task #6](src/interview/time-limited-cache) - _Time Limited Cache_ \ No newline at end of file +- [Task #6](src/interview/time-limited-cache) - _Time Limited Cache_ +- [Task #7](src/interview/concat-sources-blocks/global-type-and-title) - _Concat Sources Blocks: Global Type & Title_ \ No newline at end of file diff --git a/src/interview/concat-sources-blocks/common.ts b/src/interview/concat-sources-blocks/common.ts new file mode 100644 index 0000000..b26ba4f --- /dev/null +++ b/src/interview/concat-sources-blocks/common.ts @@ -0,0 +1,4 @@ +export interface Block { + type: string; + title: string; +} diff --git a/src/interview/concat-sources-blocks/global-type-and-title/solution.test.ts b/src/interview/concat-sources-blocks/global-type-and-title/solution.test.ts new file mode 100644 index 0000000..d7c55cd --- /dev/null +++ b/src/interview/concat-sources-blocks/global-type-and-title/solution.test.ts @@ -0,0 +1,22 @@ +import { concatSourcesBlocks } from './solution'; + +describe('Concat Sources Blocks: Global Type & Title | Interview | Testcases', () => { + test('#1', () => { + const primary = [ + { type: 'спорт', title: 'Матч' }, + { type: 'экономика', title: 'Курс валют' }, + ]; + const secondary = [ + { type: 'политика', title: 'Главная новость' }, + { type: 'культура', title: 'Главная новость' }, + ]; + const orders = ['экономика', 'политика', 'культура', 'спорт']; + const result = concatSourcesBlocks(primary, secondary, orders); + const expected = [ + { type: 'экономика', title: 'Курс валют' }, + { type: 'политика', title: 'Главная новость' }, + { type: 'спорт', title: 'Матч' }, + ]; + expect(result).toEqual(expected); + }); +}); diff --git a/src/interview/concat-sources-blocks/global-type-and-title/solution.ts b/src/interview/concat-sources-blocks/global-type-and-title/solution.ts new file mode 100644 index 0000000..8f3f109 --- /dev/null +++ b/src/interview/concat-sources-blocks/global-type-and-title/solution.ts @@ -0,0 +1,56 @@ +import { Block } from '../common'; + +/** + * @param primaryBlocks - blocks from main source + * @param secondaryBlocks - blocks from secondary sources + * @param orders - requested types in order + * @returns array of blocks, ordered by `orders`, without repeats in `types `or `titles`. + * Primary blocks are in priority. + */ +export const concatSourcesBlocks = ( + primaryBlocks: Block[], + secondaryBlocks: Block[], + orders: string[], +): Block[] => { + const result: Block[] = []; + const usedTitles = new Set(); + const usedTypes = new Set(); + + const groupByType = (blocks: Block[]): Map> => { + const map = new Map>(); + for (const block of blocks) { + const group = map.get(block.type) ?? new Set(); + group.add(block); + map.set(block.type, group); + } + return map; + }; + + const primaryMap = groupByType(primaryBlocks); + const secondaryMap = groupByType(secondaryBlocks); + + const tryToAdd = (blocks: Set | undefined): boolean => { + if (!blocks) { + return false; + } + for (const block of blocks) { + if (!usedTitles.has(block.title) && !usedTypes.has(block.type)) { + result.push(block); + usedTypes.add(block.type); + usedTitles.add(block.title); + blocks.delete(block); + return true; + } + blocks.delete(block); + } + return false; + }; + + for (const order of orders) { + if (!tryToAdd(primaryMap.get(order))) { + tryToAdd(secondaryMap.get(order)); + } + } + + return result; +};