diff --git a/src/pages/list/find-most-popular/find-most-popular.service.test.ts b/src/pages/list/find-most-popular/find-most-popular.service.test.ts new file mode 100644 index 0000000..665de8e --- /dev/null +++ b/src/pages/list/find-most-popular/find-most-popular.service.test.ts @@ -0,0 +1,65 @@ +import { expect, describe, it } from 'vitest'; +import { TopItemsList, SplitOperatorType, SortingMethod, DisplayFormat } from './service'; + +describe('TopItemsList function', () => { + it('should handle sorting alphabetically ignoring case', () => { + const input = 'Apple,banana,apple,Orange,Banana,apple'; + const result = TopItemsList('symbol', 'alphabetic', 'count', ',', input, false, true, false); + expect(result).toEqual( + 'apple: 3\n' + + 'banana: 2\n' + + 'orange: 1' + ); + }); + + it('should handle sorting by count and not ignoring case', () => { + const input = 'apple,banana,apple,orange,banana,apple,Banana'; + const result = TopItemsList('symbol', 'count', 'count', ',', input, false, false, false); + expect(result).toEqual( + 'apple: 3\n' + + 'banana: 2\n' + + 'orange: 1\n' + + 'Banana: 1' + ); + }); + + it('should handle regex split operator', () => { + const input = 'apple123banana456apple789orange012banana345apple678'; + const result = TopItemsList('regex', 'count', 'count', '\\d+', input, false, false, false); + expect(result).toEqual( + 'apple: 3\n' + + 'banana: 2\n' + + 'orange: 1' + ); + }); + + it('should handle percentage display format', () => { + const input = 'apple,banana,apple,orange,banana,apple'; + const result = TopItemsList('symbol', 'count', 'percentage', ',', input, false, false, false); + expect(result).toEqual( + 'apple: 3 (50.00%)\n' + + 'banana: 2 (33.33%)\n' + + 'orange: 1 (16.67%)' + ); + }); + + it('should handle total display format', () => { + const input = 'apple,banana,apple,orange,banana,apple'; + const result = TopItemsList('symbol', 'count', 'total', ',', input, false, false, false); + expect(result).toEqual( + 'apple: 3 (3 / 6)\n' + + 'banana: 2 (2 / 6)\n' + + 'orange: 1 (1 / 6)' + ); + }); + + it('should handle trimming and ignoring empty items', () => { + const input = ' apple , banana , apple , orange , banana , apple '; + const result = TopItemsList('symbol', 'count', 'count', ',', input, true, false, true); + expect(result).toEqual( + 'apple: 3\n' + + 'banana: 2\n' + + 'orange: 1' + ); + }); +}); \ No newline at end of file diff --git a/src/pages/list/find-most-popular/index.tsx b/src/pages/list/find-most-popular/index.tsx new file mode 100644 index 0000000..40566cf --- /dev/null +++ b/src/pages/list/find-most-popular/index.tsx @@ -0,0 +1,11 @@ +import { Box } from '@mui/material'; +import React from 'react'; +import * as Yup from 'yup'; + +const initialValues = {}; +const validationSchema = Yup.object({ + // splitSeparator: Yup.string().required('The separator is required') +}); +export default function FindMostPopular() { + return Lorem ipsum; +} \ No newline at end of file diff --git a/src/pages/list/find-most-popular/meta.ts b/src/pages/list/find-most-popular/meta.ts new file mode 100644 index 0000000..309b33c --- /dev/null +++ b/src/pages/list/find-most-popular/meta.ts @@ -0,0 +1,13 @@ +import { defineTool } from '@tools/defineTool'; +import { lazy } from 'react'; +// import image from '@assets/text.png'; + +export const tool = defineTool('list', { + name: 'Find most popular', + path: 'find-most-popular', + // image, + description: '', + shortDescription: '', + keywords: ['find', 'most', 'popular'], + component: lazy(() => import('./index')) +}); \ No newline at end of file diff --git a/src/pages/list/find-most-popular/service.ts b/src/pages/list/find-most-popular/service.ts new file mode 100644 index 0000000..6850755 --- /dev/null +++ b/src/pages/list/find-most-popular/service.ts @@ -0,0 +1,107 @@ +export type SplitOperatorType = 'symbol' | 'regex'; +export type DisplayFormat = 'count' | 'percentage' | 'total'; +export type SortingMethod = 'count' | 'alphabetic'; + +// Function that analyzes the array and returns a dict of element occurrences and handle the ignoreItemCase +function dictMaker(array: string[], + ignoreItemCase: boolean +): { [key: string]: number } { + const dict: { [key: string]: number } = {}; + for (const item of array) { + const key = ignoreItemCase ? item.toLowerCase() : item; + dict[key] = (dict[key] || 0) + 1; + } + return dict; +} + +// Function that sorts the dict created with dictMaker based on the chosen sorting method +function dictSorter( + dict: { [key: string]: number }, + sortingMethod: SortingMethod, +): { [key: string]: number } { + let sortedArray: [string, number][]; + switch (sortingMethod) { + case 'count': + sortedArray = Object.entries(dict).sort(([, countA], [, countB]) => countB - countA); + break; + case 'alphabetic': + sortedArray = Object.entries(dict).sort(([keyA], [keyB]) => { + return keyA.localeCompare(keyB) + }); + break; + default: + sortedArray = Object.entries(dict); + break; + } + return Object.fromEntries(sortedArray); +} + +// Function that prepares the output of dictSorter based on the chosen display format +function displayFormater( + dict: { [key: string]: number }, + displayFormat: DisplayFormat +): string[] { + let formattedOutput: string[] = []; + const total = Object.values(dict).reduce((acc, val) => acc + val, 0); + + switch (displayFormat) { + case 'percentage': + Object.entries(dict).forEach(([key, value]) => { + formattedOutput.push(`${key}: ${value} (${((value / total) * 100).toFixed(2)}%)`); + }); + break; + case "total": + Object.entries(dict).forEach(([key, value]) => { + formattedOutput.push(`${key}: ${value} (${value} / ${total})`); + }); + break; + case "count": + Object.entries(dict).forEach(([key, value]) => { + formattedOutput.push(`${key}: ${value}`); + }); + break; + } + return formattedOutput; +} + +export function TopItemsList( + splitOperatorType: SplitOperatorType, + sortingMethod: SortingMethod, + displayFormat: DisplayFormat, + splitSeparator: string, + input: string, + deleteEmptyItems: boolean, + ignoreItemCase: boolean, + trimItems: boolean +): string { + let array: string[]; + switch (splitOperatorType) { + case 'symbol': + array = input.split(splitSeparator); + break; + case 'regex': + array = input.split(new RegExp(splitSeparator)).filter(item => item !== ''); + break; + } + + // Trim items if required + if (trimItems) { + array = array.map(item => item.trim()); + } + + // Delete empty items after initial split + if (deleteEmptyItems) { + array = array.filter(item => item !== ''); + } + + // Transform the array into dict + const unsortedDict = dictMaker(array, ignoreItemCase); + + // Sort the list if required + const sortedDict = dictSorter(unsortedDict, sortingMethod); + + // Format the output with desired format + const formattedOutput = displayFormater(sortedDict, displayFormat); + + return formattedOutput.join('\n'); +} diff --git a/src/pages/list/index.ts b/src/pages/list/index.ts index d265dd7..eb8c9f6 100644 --- a/src/pages/list/index.ts +++ b/src/pages/list/index.ts @@ -1,3 +1,4 @@ +import { tool as listFindMostPopular } from './find-most-popular/meta'; import { tool as listGroup } from './group/meta'; import { tool as listWrap } from './wrap/meta'; import { tool as listRotate } from './rotate/meta';