From 22cdc82e3fd398b9cd5c98daaf13fb742db4914d Mon Sep 17 00:00:00 2001 From: Babar Saleh Hayat Date: Wed, 27 Mar 2024 19:29:53 +0500 Subject: [PATCH 1/2] feat(new tool): Implement JSON sorter, addressing issue #941 --- components.d.ts | 17 ++++ locales/en.yml | 4 + locales/es.yml | 6 +- locales/fr.yml | 4 + locales/pt.yml | 4 + locales/uk.yml | 4 + locales/vi.yml | 4 + locales/zh.yml | 4 + src/tools/index.ts | 2 + src/tools/json-sort-master/index.ts | 13 +++ .../json-sort-master/json-sort-master.vue | 80 +++++++++++++++++++ src/tools/json-sort-master/json.models.ts | 74 +++++++++++++++++ 12 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 src/tools/json-sort-master/index.ts create mode 100644 src/tools/json-sort-master/json-sort-master.vue create mode 100644 src/tools/json-sort-master/json.models.ts diff --git a/components.d.ts b/components.d.ts index e31119b3..e34c50e1 100644 --- a/components.d.ts +++ b/components.d.ts @@ -89,7 +89,9 @@ declare module '@vue/runtime-core' { HttpStatusCodes: typeof import('./src/tools/http-status-codes/http-status-codes.vue')['default'] IbanValidatorAndParser: typeof import('./src/tools/iban-validator-and-parser/iban-validator-and-parser.vue')['default'] 'IconMdi:brushVariant': typeof import('~icons/mdi/brush-variant')['default'] + 'IconMdi:contentCopy': typeof import('~icons/mdi/content-copy')['default'] 'IconMdi:kettleSteamOutline': typeof import('~icons/mdi/kettle-steam-outline')['default'] + IconMdiArrowDown: typeof import('~icons/mdi/arrow-down')['default'] IconMdiChevronDown: typeof import('~icons/mdi/chevron-down')['default'] IconMdiChevronRight: typeof import('~icons/mdi/chevron-right')['default'] IconMdiClose: typeof import('~icons/mdi/close')['default'] @@ -108,6 +110,7 @@ declare module '@vue/runtime-core' { Ipv6UlaGenerator: typeof import('./src/tools/ipv6-ula-generator/ipv6-ula-generator.vue')['default'] JsonDiff: typeof import('./src/tools/json-diff/json-diff.vue')['default'] JsonMinify: typeof import('./src/tools/json-minify/json-minify.vue')['default'] + JsonSortMaster: typeof import('./src/tools/json-sort-master/json-sort-master.vue')['default'] JsonToCsv: typeof import('./src/tools/json-to-csv/json-to-csv.vue')['default'] JsonToToml: typeof import('./src/tools/json-to-toml/json-to-toml.vue')['default'] JsonToYaml: typeof import('./src/tools/json-to-yaml-converter/json-to-yaml.vue')['default'] @@ -126,25 +129,38 @@ declare module '@vue/runtime-core' { MenuLayout: typeof import('./src/components/MenuLayout.vue')['default'] MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default'] MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default'] + NAlert: typeof import('naive-ui')['NAlert'] NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default'] + NCheckbox: typeof import('naive-ui')['NCheckbox'] NCode: typeof import('naive-ui')['NCode'] NCollapseTransition: typeof import('naive-ui')['NCollapseTransition'] + NColorPicker: typeof import('naive-ui')['NColorPicker'] NConfigProvider: typeof import('naive-ui')['NConfigProvider'] + NDatePicker: typeof import('naive-ui')['NDatePicker'] NDivider: typeof import('naive-ui')['NDivider'] NEllipsis: typeof import('naive-ui')['NEllipsis'] + NForm: typeof import('naive-ui')['NForm'] NFormItem: typeof import('naive-ui')['NFormItem'] NGi: typeof import('naive-ui')['NGi'] NGrid: typeof import('naive-ui')['NGrid'] NH1: typeof import('naive-ui')['NH1'] NH3: typeof import('naive-ui')['NH3'] NIcon: typeof import('naive-ui')['NIcon'] + NImage: typeof import('naive-ui')['NImage'] + NInputGroup: typeof import('naive-ui')['NInputGroup'] + NInputGroupLabel: typeof import('naive-ui')['NInputGroupLabel'] NInputNumber: typeof import('naive-ui')['NInputNumber'] NLabel: typeof import('naive-ui')['NLabel'] 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'] NSpin: typeof import('naive-ui')['NSpin'] + NStatistic: typeof import('naive-ui')['NStatistic'] + NSwitch: typeof import('naive-ui')['NSwitch'] + NTable: typeof import('naive-ui')['NTable'] + NTag: typeof import('naive-ui')['NTag'] 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'] PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default'] @@ -159,6 +175,7 @@ declare module '@vue/runtime-core' { RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] RsaKeyPairGenerator: typeof import('./src/tools/rsa-key-pair-generator/rsa-key-pair-generator.vue')['default'] + SafelinkDecoder: typeof import('./src/tools/safelink-decoder/safelink-decoder.vue')['default'] SlugifyString: typeof import('./src/tools/slugify-string/slugify-string.vue')['default'] SpanCopyable: typeof import('./src/components/SpanCopyable.vue')['default'] SqlPrettify: typeof import('./src/tools/sql-prettify/sql-prettify.vue')['default'] diff --git a/locales/en.yml b/locales/en.yml index 50d48af9..c038dad9 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -328,6 +328,10 @@ tools: title: PDF signature checker description: Verify the signatures of a PDF file. A signed PDF file contains one or more signatures that may be used to determine whether the contents of the file have been altered since the file was signed. + json-sort-master: + title: JSON sort master + description: Sort your JSON by keys and values. + json-minify: title: JSON minify description: Minify and compress your JSON by removing unnecessary white spaces. diff --git a/locales/es.yml b/locales/es.yml index b87502fc..15683602 100644 --- a/locales/es.yml +++ b/locales/es.yml @@ -68,4 +68,8 @@ tools: math: Math measurement: Measurement text: Text - data: Data \ No newline at end of file + data: Data + + json-sort-master: + title: Maestro de clasificación JSON + description: Ordena tu JSON por claves y valores. \ No newline at end of file diff --git a/locales/fr.yml b/locales/fr.yml index 35c7df2e..7f25d371 100644 --- a/locales/fr.yml +++ b/locales/fr.yml @@ -79,3 +79,7 @@ tools: copied: Le token a été copié length: Longueur tokenPlaceholder: Le token... + + json-sort-master: + title: Maître de tri JSON + description: Triez votre JSON par clés et valeurs. \ No newline at end of file diff --git a/locales/pt.yml b/locales/pt.yml index 96fdaed4..e953a3ea 100644 --- a/locales/pt.yml +++ b/locales/pt.yml @@ -69,3 +69,7 @@ tools: measurement: 'Medidas' text: 'Texto' data: 'Dados' + + json-sort-master: + title: 'Mestre de classificação JSON' + description: 'Ordene seu JSON por chaves e valores.' diff --git a/locales/uk.yml b/locales/uk.yml index 2d28f157..449eeb94 100644 --- a/locales/uk.yml +++ b/locales/uk.yml @@ -69,3 +69,7 @@ tools: measurement: Вимірювання text: Текст data: Дані + + json-sort-master: + title: JSON сортувальник + description: Сортуйте ваш JSON за ключами та значеннями. diff --git a/locales/vi.yml b/locales/vi.yml index 9eb16bf0..3131c1b7 100644 --- a/locales/vi.yml +++ b/locales/vi.yml @@ -221,6 +221,10 @@ tools: title: Định dạng và làm đẹp JSON description: Định dạng chuỗi JSON của bạn thành một định dạng dễ đọc và thân thiện với con người. + json-sort-master: + title: Sắp xếp JSON + description: Sắp xếp JSON của bạn theo các khóa và giá trị. + docker-run-to-docker-compose-converter: title: Chuyển đổi lệnh docker run thành tệp docker-compose description: Chuyển đổi các lệnh docker run thành tệp docker-compose! diff --git a/locales/zh.yml b/locales/zh.yml index 160fe1fa..f1c06bde 100644 --- a/locales/zh.yml +++ b/locales/zh.yml @@ -225,6 +225,10 @@ tools: title: JSON美化和格式化 description: 将JSON字符串修饰为友好的可读格式。 + json-sort-master: + title: JSON排序大师 + description: 按键和值对您的JSON进行排序。 + docker-run-to-docker-compose-converter: title: Docker Run 到 docker-compose 转换器 description: 将 docker run 命令行转换为 docker-compose 文件! diff --git a/src/tools/index.ts b/src/tools/index.ts index aa861c93..e3896bd4 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -1,6 +1,7 @@ import { tool as base64FileConverter } from './base64-file-converter'; import { tool as base64StringConverter } from './base64-string-converter'; import { tool as basicAuthGenerator } from './basic-auth-generator'; +import { tool as jsonSortMaster } from './json-sort-master'; import { tool as asciiTextDrawer } from './ascii-text-drawer'; @@ -104,6 +105,7 @@ export const toolsByCategory: ToolCategory[] = [ yamlToToml, jsonToYaml, jsonToToml, + jsonSortMaster, listConverter, tomlToJson, tomlToYaml, diff --git a/src/tools/json-sort-master/index.ts b/src/tools/json-sort-master/index.ts new file mode 100644 index 00000000..5bb84d1c --- /dev/null +++ b/src/tools/json-sort-master/index.ts @@ -0,0 +1,13 @@ +import { ArrowsSort } from '@vicons/tabler'; +import { defineTool } from '../tool'; +import { translate } from '@/plugins/i18n.plugin'; + +export const tool = defineTool({ + name: translate('tools.json-sort-master.title'), + path: '/json-sort-master', + description: translate('tools.json-sort-master.description'), + keywords: ['json', 'sort'], + component: () => import('./json-sort-master.vue'), + icon: ArrowsSort, + createdAt: new Date('2024-03-27'), +}); diff --git a/src/tools/json-sort-master/json-sort-master.vue b/src/tools/json-sort-master/json-sort-master.vue new file mode 100644 index 00000000..28d5f1c9 --- /dev/null +++ b/src/tools/json-sort-master/json-sort-master.vue @@ -0,0 +1,80 @@ + + + + + diff --git a/src/tools/json-sort-master/json.models.ts b/src/tools/json-sort-master/json.models.ts new file mode 100644 index 00000000..de487e49 --- /dev/null +++ b/src/tools/json-sort-master/json.models.ts @@ -0,0 +1,74 @@ +import { type MaybeRef, get } from '@vueuse/core'; +import JSON5 from 'json5'; + +export { sortObjectKeys, sortObjectValues, formatJson }; + +function sortObjectKeys(obj: T, sortMethod: string): T { + if (typeof obj !== 'object' || obj === null) { + return obj; + } + + if (Array.isArray(obj)) { + return obj.map(value => sortObjectKeys(value, sortMethod)) as unknown as T; + } + + return Object.keys(obj) + .sort((a, b) => sortMethod === 'key_name' ? a.localeCompare(b) : b.localeCompare(a)) + .reduce((sortedObj, key) => { + sortedObj[key] = sortObjectKeys((obj as Record)[key], sortMethod); + return sortedObj; + }, {} as Record) as T; +} + +function sortObjectValues(obj: any, sortMethod: string, keyName: string): any { + if (Array.isArray(obj)) { + return obj.sort((a, b) => { + const valueA = a[keyName]; + const valueB = b[keyName]; + + if (typeof valueA === 'number' && typeof valueB === 'number') { + return sortMethod === 'key_val' ? valueA - valueB : valueB - valueA; + } + if (typeof valueA === 'string' && typeof valueB === 'string') { + return sortMethod === 'key_val' ? valueA.localeCompare(valueB) : valueB.localeCompare(valueA); + } + return 0; + }); + } + + if (typeof obj === 'object' && obj !== null) { + const sortedKeys = Object.keys(obj).sort((a, b) => { + if (sortMethod === 'key_val') { + return a.localeCompare(b); + } + return b.localeCompare(a); + }); + + return sortedKeys.reduce((sortedObj, key) => { + sortedObj[key] = sortObjectValues((obj as Record)[key], sortMethod, keyName); + return sortedObj; + }, {} as Record) as T; + } + + return obj; +} + +function formatJson({ + rawJson, + sortMethod = 'key_name', + keyName = '', + indentSize = 3, +}: { + rawJson: MaybeRef + sortMethod: MaybeRef + keyName: MaybeRef + indentSize?: MaybeRef +}) { + const parsedObject = JSON5.parse(get(rawJson)); + + if (['key_name', 'key_name_desc'].includes(get(sortMethod))) { + return JSON.stringify(sortObjectKeys(parsedObject, get(sortMethod)), null, get(indentSize)); + } + + return JSON.stringify(sortObjectValues(parsedObject, get(sortMethod), get(keyName)), null, get(indentSize)); +} From df343d11f5f89f6dd150712a7470340663a5695b Mon Sep 17 00:00:00 2001 From: Babar Saleh Hayat Date: Wed, 27 Mar 2024 22:09:57 +0500 Subject: [PATCH 2/2] Fix type --- src/tools/json-sort-master/json.models.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/json-sort-master/json.models.ts b/src/tools/json-sort-master/json.models.ts index de487e49..b8e24889 100644 --- a/src/tools/json-sort-master/json.models.ts +++ b/src/tools/json-sort-master/json.models.ts @@ -20,7 +20,7 @@ function sortObjectKeys(obj: T, sortMethod: string): T { }, {} as Record) as T; } -function sortObjectValues(obj: any, sortMethod: string, keyName: string): any { +function sortObjectValues(obj: T, sortMethod: string, keyName: string): T { if (Array.isArray(obj)) { return obj.sort((a, b) => { const valueA = a[keyName];