Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,5 @@ Interview Questions
- [Task #8](src/interview/concat-sources-blocks/primary-and-secondary-conflicts) - _Concat Sources Blocks: Primary & Secondary Conflicts_
- [Task #9](src/interview/render-items/no-async) - _Render Items: No Async_
- [Task #10](src/interview/render-items/with-async) - _Render Items: With Async_
- [Task #11](src/interview/build-tree/no-replies-sort) - _Build Tree: No Replies Sort_
- [Task #11](src/interview/build-tree/no-replies-sort) - _Build Tree: No Replies Sort_
- [Task #12](src/interview/build-tree/with-replies-sort) - _Build Tree: With Replies Sort_
9 changes: 9 additions & 0 deletions src/interview/build-tree/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export interface Message {
message: string;
pass: string;
}

export interface NestedMessage {
message: string;
replies?: NestedMessage[];
}
10 changes: 1 addition & 9 deletions src/interview/build-tree/no-replies-sort/solution.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
interface Message {
message: string;
pass: string;
}

interface NestedMessage {
message: string;
replies?: NestedMessage[];
}
import { Message, NestedMessage } from '../common';

/**
* @param messages - arrays of messages
Expand Down
102 changes: 102 additions & 0 deletions src/interview/build-tree/with-replies-sort/solution.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { buildTree } from './solution';

describe('Build Tree: Sorted Replies (numeric)', () => {
test('#1 Should sort children numerically: 1.2 before 1.10', () => {
const messages = [
{ message: 'Root', pass: '1' },
{ message: 'Child 10', pass: '1.10' },
{ message: 'Child 2', pass: '1.2' },
];
const result = buildTree(messages);
expect(result[0].replies?.map((r) => r.message)).toEqual([
'Child 2',
'Child 10',
]);
});

test('#2 Should sort children on multiple levels', () => {
const messages = [
{ message: 'Root', pass: '1' },
{ message: 'Child 10', pass: '1.10' },
{ message: 'Child 2', pass: '1.2' },
{ message: 'Child 1', pass: '1.1' },
{ message: 'Grandchild 10', pass: '1.1.10' },
{ message: 'Grandchild 2', pass: '1.1.2' },
];
const result = buildTree(messages);
const root = result[0];
expect(root.replies?.map((r) => r.message)).toEqual([
'Child 1',
'Child 2',
'Child 10',
]);
const rootReplies = root.replies ?? [];
expect(rootReplies[0].replies?.map((r) => r.message)).toEqual([
'Grandchild 2',
'Grandchild 10',
]);
});

test('#3 Should sort children when children have different depths', () => {
const messages = [
{ message: 'Root', pass: '1' },
{ message: 'A', pass: '1.2' },
{ message: 'B', pass: '1.10' },
{ message: 'C', pass: '1.1' },
{ message: 'D', pass: '1.1.1' },
{ message: 'E', pass: '1.1.10' },
{ message: 'F', pass: '1.1.2' },
];
const result = buildTree(messages);
expect(result[0].replies?.map((r) => r.message)).toEqual(['C', 'A', 'B']);
expect((result[0].replies ?? [])[0].replies?.map((r) => r.message)).toEqual(
['D', 'F', 'E'],
);
});

test('#4 Should not sort roots (preserve original order)', () => {
const messages = [
{ message: 'Root2', pass: '2' },
{ message: 'Root1', pass: '1' },
{ message: 'Child 10', pass: '1.10' },
{ message: 'Child 2', pass: '1.2' },
];
const result = buildTree(messages);
expect(result.map((r) => r.message)).toEqual(['Root2', 'Root1']);
const root1 = result.find((r) => r.message === 'Root1');
expect(root1?.replies?.map((r) => r.message)).toEqual([
'Child 2',
'Child 10',
]);
});

test('#5 Should sort children even if parent appears after children', () => {
const messages = [
{ message: 'Child 10', pass: '1.10' },
{ message: 'Child 2', pass: '1.2' },
{ message: 'Root', pass: '1' },
];
const result = buildTree(messages);
expect(result[0].replies?.map((r) => r.message)).toEqual([
'Child 2',
'Child 10',
]);
});

test('#6 Should handle numeric sorting with more than two digits', () => {
const messages = [
{ message: 'Root', pass: '1' },
{ message: 'Child 2', pass: '1.2' },
{ message: 'Child 10', pass: '1.10' },
{ message: 'Child 1', pass: '1.1' },
{ message: 'Child 100', pass: '1.100' },
];
const result = buildTree(messages);
expect(result[0].replies?.map((r) => r.message)).toEqual([
'Child 1',
'Child 2',
'Child 10',
'Child 100',
]);
});
});
61 changes: 61 additions & 0 deletions src/interview/build-tree/with-replies-sort/solution.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Message, NestedMessage } from '../common';

/**
* @param messages - arrays of messages
* @returns array of nested messages with sorted
* @example
*
* const messages = [
* { Message: 'Child 10', Pass: '1.10' },
* { Message: 'Child 2', Pass: '1.2' },
* { Message: 'Root', Pass: '1' },
* { Message: 'Child 1', Pass: '1.1' },
* ];
*
* console.log(JSON.stringify(buildMessageTree(messages), null, 2));
*
* // In Replies of root children must be sorted:
* // Child 1 -> Child 2 -> Child 10
*/
export const buildTree = (messages: Message[]): NestedMessage[] => {
const msgMap = new Map<
string,
{ message: NestedMessage; parentPath: string | null }
>();
const ordersMap = new Map<NestedMessage, number>();
const roots: NestedMessage[] = [];
const groups: NestedMessage[][] = [];
for (const msg of messages) {
const message = { message: msg.message };
const parentPathIdx = msg.pass.lastIndexOf('.');
msgMap.set(msg.pass, {
message,
parentPath:
parentPathIdx === -1 ? null : msg.pass.slice(0, parentPathIdx),
});
ordersMap.set(message, Number(msg.pass.slice(parentPathIdx + 1)));
}

for (const { message, parentPath } of msgMap.values()) {
if (parentPath === null) {
roots.push(message);
continue;
}
const parent = msgMap.get(parentPath)?.message;
if (!parent) {
roots.push(message);
continue;
}
if (!parent.replies) {
parent.replies = [];
groups.push(parent.replies);
}
parent.replies.push(message);
}

for (const group of groups) {
group.sort((a, b) => (ordersMap.get(a) ?? 1) - (ordersMap.get(b) ?? 1));
}

return roots;
};
Loading