Skip to content

Commit

Permalink
feat(new tool): List Comparaison
Browse files Browse the repository at this point in the history
Fix #817
  • Loading branch information
sharevb committed Sep 22, 2024
1 parent 318fb6e commit d6ce833
Show file tree
Hide file tree
Showing 8 changed files with 273 additions and 7 deletions.
4 changes: 4 additions & 0 deletions components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ declare module '@vue/runtime-core' {
JsonViewer: typeof import('./src/tools/json-viewer/json-viewer.vue')['default']
JwtParser: typeof import('./src/tools/jwt-parser/jwt-parser.vue')['default']
KeycodeInfo: typeof import('./src/tools/keycode-info/keycode-info.vue')['default']
ListComparer: typeof import('./src/tools/list-comparer/list-comparer.vue')['default']
ListConverter: typeof import('./src/tools/list-converter/list-converter.vue')['default']
LocaleSelector: typeof import('./src/modules/i18n/components/locale-selector.vue')['default']
LoremIpsumGenerator: typeof import('./src/tools/lorem-ipsum-generator/lorem-ipsum-generator.vue')['default']
Expand All @@ -132,18 +133,21 @@ declare module '@vue/runtime-core' {
NCode: typeof import('naive-ui')['NCode']
NCollapseTransition: typeof import('naive-ui')['NCollapseTransition']
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
NDivider: typeof import('naive-ui')['NDivider']
NEllipsis: typeof import('naive-ui')['NEllipsis']
NForm: typeof import('naive-ui')['NForm']
NFormItem: typeof import('naive-ui')['NFormItem']
NH1: typeof import('naive-ui')['NH1']
NH3: typeof import('naive-ui')['NH3']
NIcon: typeof import('naive-ui')['NIcon']
NInput: typeof import('naive-ui')['NInput']
NInputNumber: typeof import('naive-ui')['NInputNumber']
NLayout: typeof import('naive-ui')['NLayout']
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
NMenu: typeof import('naive-ui')['NMenu']
NScrollbar: typeof import('naive-ui')['NScrollbar']
NSlider: typeof import('naive-ui')['NSlider']
NSpace: typeof import('naive-ui')['NSpace']
NSwitch: typeof import('naive-ui')['NSwitch']
NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default']
OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default']
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"dompurify": "^3.0.6",
"email-normalizer": "^1.0.0",
"emojilib": "^3.0.10",
"fast-array-diff": "^1.1.0",
"figlet": "^1.7.0",
"figue": "^1.2.0",
"fuse.js": "^6.6.2",
Expand Down
21 changes: 14 additions & 7 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/tools/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { tool as tomlToJson } from './toml-to-json';
import { tool as jsonToCsv } from './json-to-csv';
import { tool as cameraRecorder } from './camera-recorder';
import { tool as listConverter } from './list-converter';
import { tool as listComparer } from './list-comparer';
import { tool as phoneParserAndFormatter } from './phone-parser-and-formatter';
import { tool as jsonDiff } from './json-diff';
import { tool as ipv4RangeExpander } from './ipv4-range-expander';
Expand Down Expand Up @@ -108,6 +109,7 @@ export const toolsByCategory: ToolCategory[] = [
jsonToYaml,
jsonToToml,
listConverter,
listComparer,
tomlToJson,
tomlToYaml,
xmlToJson,
Expand Down
12 changes: 12 additions & 0 deletions src/tools/list-comparer/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { List } from '@vicons/tabler';
import { defineTool } from '../tool';

export const tool = defineTool({
name: 'Lists Comparer',
path: '/list-comparer',
description: 'Compare two list items',
keywords: ['list', 'comparer'],
component: () => import('./list-comparer.vue'),
icon: List,
createdAt: new Date('2024-08-15'),
});
108 changes: 108 additions & 0 deletions src/tools/list-comparer/list-comparer.service.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { describe, expect, it } from 'vitest';
import { compareLists } from './list-comparer.service';

describe('list-comparer', () => {
describe('compareLists', () => {
it('return correct comparaison', () => {
expect(compareLists({
list1: '1\n 2\n3\n4\n5\n4\n7\nA',
list2: '1\n2\n3\n4\n6\n4\n7\na',
trimItems: true,
ignoreCase: true,
})).to.deep.eq({
list1Not2: [
'5',
],
list2Not1: [
'6',
],
same: [
'1',
'2',
'3',
'4',
'4',
'7',
'a',
],
});

expect(compareLists({
list1: '1\n 2\n3\n4\n5\n4\n7\nA',
list2: '1\n2\n3\n4\n6\n4\n7\na',
trimItems: false,
ignoreCase: false,
})).to.deep.eq({
list1Not2: [
' 2',
'5',
'A',
],
list2Not1: [
'2',
'6',
'a',
],
same: [
'1',
'3',
'4',
'4',
'7',
],
});

expect(compareLists({
list1: '1\n 2\n3\n4\n5\n4\n7\nA\nA',
list2: '1\n2\n3\n4\n6\n4\n7\na',
trimItems: false,
ignoreCase: false,
noDuplicate: true,
})).to.deep.eq({
list1Not2: [
' 2',
'5',
'A',
],
list2Not1: [
'2',
'6',
'a',
],
same: [
'1',
'3',
'4',
'7',
],
});

expect(compareLists({
list1: '1, 2,3,4,5,4,7,A,A',
list2: '1\n2\n3\n4\n6\n4\n7\na',
trimItems: false,
ignoreCase: false,
separator: ',',
})).to.deep.eq({
list1Not2: [
' 2',
'5',
'A',
'A',
],
list2Not1: [
'2',
'6',
'a',
],
same: [
'1',
'3',
'4',
'4',
'7',
],
});
});
});
});
43 changes: 43 additions & 0 deletions src/tools/list-comparer/list-comparer.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import _ from 'lodash';
import * as diff from 'fast-array-diff';

export function compareLists({
list1,
list2,
ignoreCase = false,
trimItems = true,
noDuplicate = false,
separator = '',
}: {
list1: string
list2: string
separator?: string
ignoreCase?: boolean
trimItems?: boolean
noDuplicate?: boolean
}) {
const prepareList = (list: string) =>
_.chain(list ?? '')
.thru(text => ignoreCase ? text.toLowerCase() : text)
.split(new RegExp(`[\n${separator}]`, 'g'))
.map(text => trimItems ? text.trim() : text)
.value();

const list1Arr = prepareList(list1);
const list2Arr = prepareList(list2);

const arrSame = diff.same(list1Arr, list2Arr);
const arrDifferences = diff.diff(list1Arr, list2Arr);
if (noDuplicate) {
return {
same: [...new Set(arrSame)],
list2Not1: [...new Set(arrDifferences.added)],
list1Not2: [...new Set(arrDifferences.removed)],
};
}
return {
same: arrSame,
list2Not1: arrDifferences.added,
list1Not2: arrDifferences.removed,
};
}
89 changes: 89 additions & 0 deletions src/tools/list-comparer/list-comparer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<script setup lang="ts">
import { compareLists } from './list-comparer.service';
const compareConfig = useStorage<{ ignoreCase: boolean; trimItems: boolean; noDuplicate: boolean; separator: string }>('list-cmp:conf', {
ignoreCase: false,
trimItems: true,
noDuplicate: false,
separator: '',
});
const list1 = ref('');
const list2 = ref('');
const compareResult = computed(() => {
return compareLists({
list1: list1.value,
list2: list2.value,
ignoreCase: compareConfig.value.ignoreCase,
trimItems: compareConfig.value.trimItems,
noDuplicate: compareConfig.value.noDuplicate,
separator: compareConfig.value.separator,
});
});
</script>

<template>
<div>
<n-space justify="center" gap-1 align="baseline">
<n-form-item
label="Trim items"
label-placement="left"
>
<n-switch v-model:value="compareConfig.trimItems" />
</n-form-item>

<n-form-item
label="Ignore case"
label-placement="left"
mb-2
>
<n-switch v-model:value="compareConfig.ignoreCase" />
</n-form-item>
<n-form-item
label="No duplicate"
label-placement="left"
>
<n-switch v-model:value="compareConfig.noDuplicate" />
</n-form-item>

<n-form-item
label="Separator"
label-placement="left"
>
<n-input
v-model:value="compareConfig.separator"
placeholder="Additional separator"
/>
</n-form-item>
</n-space>

<div flex gap-1>
<c-input-text
v-model:value="list1"
multiline
rows="10"
label="List 1"
/>
<c-input-text
v-model:value="list2"
multiline
rows="10"
label="List 2"
/>
</div>

<div v-if="list1 || list2">
<n-divider />

<c-card title="Items in both lists" mb-2>
<textarea-copyable :value="compareResult.same.join('\n')" />
</c-card>
<c-card title="Items in List 1 but not in List 2" mb-2>
<textarea-copyable :value="compareResult.list1Not2.join('\n')" />
</c-card>
<c-card title="Items in List 2 but not in List 1" mb-2>
<textarea-copyable :value="compareResult.list2Not1.join('\n')" />
</c-card>
</div>
</div>
</template>

0 comments on commit d6ce833

Please sign in to comment.