Skip to content

Commit d02443a

Browse files
committed
fix(json-to-csv): handle single object and flatten
Fix CorentinTh#1203
1 parent 87984e2 commit d02443a

File tree

6 files changed

+77
-11
lines changed

6 files changed

+77
-11
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
"@tiptap/pm": "2.1.6",
4242
"@tiptap/starter-kit": "2.1.6",
4343
"@tiptap/vue-3": "2.0.3",
44-
"@types/markdown-it": "^13.0.7",
4544
"@types/figlet": "^1.5.8",
45+
"@types/markdown-it": "^13.0.7",
4646
"@vicons/material": "^0.12.0",
4747
"@vicons/tabler": "^0.12.0",
4848
"@vueuse/core": "^10.3.0",
@@ -62,6 +62,7 @@
6262
"emojilib": "^3.0.10",
6363
"figlet": "^1.7.0",
6464
"figue": "^1.2.0",
65+
"flatten-anything": "^4.0.1",
6566
"fuse.js": "^6.6.2",
6667
"highlight.js": "^11.7.0",
6768
"iarna-toml-esm": "^3.0.5",

pnpm-lock.yaml

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
declare module "flatten-anything" {
2+
export function flatten(objectOrArray: {
3+
[key in string]: any;
4+
}, untilDepth?: number): {
5+
[key in string]: any;
6+
};
7+
}

src/tools/json-to-csv/json-to-csv.service.test.ts

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import { convertArrayToCsv, getHeaders } from './json-to-csv.service';
33

44
describe('json-to-csv service', () => {
55
describe('getHeaders', () => {
6+
it('extracts all the keys from the array of nested objects', () => {
7+
expect(getHeaders({ array: [{ a: { c: 1, d: 1 }, b: 2 }, { a: 3, c: 4 }] })).toEqual(['a.c', 'a.d', 'b', 'a', 'c']);
8+
});
9+
610
it('extracts all the keys from the array of objects', () => {
711
expect(getHeaders({ array: [{ a: 1, b: 2 }, { a: 3, c: 4 }] })).toEqual(['a', 'b', 'c']);
812
});
@@ -20,7 +24,7 @@ describe('json-to-csv service', () => {
2024

2125
];
2226

23-
expect(convertArrayToCsv({ array })).toMatchInlineSnapshot(`
27+
expect(convertArrayToCsv({ arrayOrObject: array })).toMatchInlineSnapshot(`
2428
"a,b
2529
1,2
2630
3,4"
@@ -33,7 +37,7 @@ describe('json-to-csv service', () => {
3337
{ a: 3, c: 4 },
3438
];
3539

36-
expect(convertArrayToCsv({ array })).toMatchInlineSnapshot(`
40+
expect(convertArrayToCsv({ arrayOrObject: array })).toMatchInlineSnapshot(`
3741
"a,b,c
3842
1,2,
3943
3,,4"
@@ -45,7 +49,7 @@ describe('json-to-csv service', () => {
4549
{ a: null, b: 2 },
4650
];
4751

48-
expect(convertArrayToCsv({ array })).toMatchInlineSnapshot(`
52+
expect(convertArrayToCsv({ arrayOrObject: array })).toMatchInlineSnapshot(`
4953
"a,b
5054
null,2"
5155
`);
@@ -57,7 +61,7 @@ describe('json-to-csv service', () => {
5761
{ b: 3 },
5862
];
5963

60-
expect(convertArrayToCsv({ array })).toMatchInlineSnapshot(`
64+
expect(convertArrayToCsv({ arrayOrObject: array })).toMatchInlineSnapshot(`
6165
"a,b
6266
,2
6367
,3"
@@ -69,7 +73,7 @@ describe('json-to-csv service', () => {
6973
{ a: 'hello, world', b: 2 },
7074
];
7175

72-
expect(convertArrayToCsv({ array })).toMatchInlineSnapshot(`
76+
expect(convertArrayToCsv({ arrayOrObject: array })).toMatchInlineSnapshot(`
7377
"a,b
7478
\\"hello, world\\",2"
7579
`);
@@ -80,10 +84,32 @@ describe('json-to-csv service', () => {
8084
{ a: 'hello "world"', b: 2 },
8185
];
8286

83-
expect(convertArrayToCsv({ array })).toMatchInlineSnapshot(`
87+
expect(convertArrayToCsv({ arrayOrObject: array })).toMatchInlineSnapshot(`
8488
"a,b
8589
hello \\\\\\"world\\\\\\",2"
8690
`);
8791
});
92+
93+
it('converts an array of nested objects to a CSV string', () => {
94+
const array = [
95+
{ a: { c: 1, d: 1 }, b: 2 },
96+
{ a: 3, c: 4 },
97+
];
98+
99+
expect(convertArrayToCsv({ arrayOrObject: array })).toMatchInlineSnapshot(`
100+
"a.c,a.d,b,a,c
101+
1,1,2,,
102+
,,,3,4"
103+
`);
104+
});
105+
106+
it('converts an object to a CSV string', () => {
107+
const obj = { a: { c: 1, d: 1 }, b: 2 };
108+
109+
expect(convertArrayToCsv({ arrayOrObject: obj })).toMatchInlineSnapshot(`
110+
"a.c,a.d,b
111+
1,1,2"
112+
`);
113+
});
88114
});
89115
});

src/tools/json-to-csv/json-to-csv.service.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import { flatten } from 'flatten-anything';
2+
13
export { getHeaders, convertArrayToCsv };
24

35
function getHeaders({ array }: { array: Record<string, unknown>[] }): string[] {
46
const headers = new Set<string>();
57

6-
array.forEach(item => Object.keys(item).forEach(key => headers.add(key)));
8+
array.forEach(item => Object.keys(flatten(item)).forEach(key => headers.add(key)));
79

810
return Array.from(headers);
911
}
@@ -26,10 +28,12 @@ function serializeValue(value: unknown): string {
2628
return valueAsString;
2729
}
2830

29-
function convertArrayToCsv({ array }: { array: Record<string, unknown>[] }): string {
31+
function convertArrayToCsv({ arrayOrObject }: { arrayOrObject: Record<string, unknown>[] | Record<string, unknown> }): string {
32+
const array = !Array.isArray(arrayOrObject) ? [arrayOrObject] : arrayOrObject;
33+
3034
const headers = getHeaders({ array });
3135

32-
const rows = array.map(item => headers.map(header => serializeValue(item[header])));
36+
const rows = array.map(item => headers.map(header => serializeValue(flatten(item)[header])));
3337

3438
return [headers.join(','), ...rows].join('\n');
3539
}

src/tools/json-to-csv/json-to-csv.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ function transformer(value: string) {
99
if (value === '') {
1010
return '';
1111
}
12-
return convertArrayToCsv({ array: JSON5.parse(value) });
12+
return convertArrayToCsv({ arrayOrObject: JSON5.parse(value) });
1313
}, '');
1414
}
1515

0 commit comments

Comments
 (0)