Skip to content

Commit 9d00d53

Browse files
create function transposeObjectArray (#31)
* create function partitionObjectArray * lint * pre-commit * rename to transposeObjectArray * lint * fix test
1 parent 3b91c10 commit 9d00d53

File tree

5 files changed

+216
-3
lines changed

5 files changed

+216
-3
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"author": "Transcend Inc.",
33
"name": "@transcend-io/type-utils",
44
"description": "Small package containing useful typescript utilities.",
5-
"version": "1.7.1",
5+
"version": "1.8.0",
66
"homepage": "https://github.com/transcend-io/type-utils",
77
"repository": {
88
"type": "git",

src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ export * from './types';
1212
export * from './valuesOf';
1313
export * from './findAllWithRegex';
1414
export * from './flattenObject';
15-
export * from './aggregateObjects';
15+
export * from './aggregateObjects';
16+
export * from './transposeObjectArray';

src/tests/aggregateObjects.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { expect } from 'chai';
22
import { aggregateObjects } from '../aggregateObjects';
33

4-
describe.only('aggregateObjects', () => {
4+
describe('aggregateObjects', () => {
55
it('should return empty object for empty input array', () => {
66
const result = aggregateObjects({ objs: [] });
77
expect(result).to.deep.equal({});
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import { expect } from 'chai';
2+
import { transposeObjectArray } from '../transposeObjectArray';
3+
4+
describe('transposeObjectArray', () => {
5+
it('should handle empty array', () => {
6+
const result = transposeObjectArray([], ['id', 'name']);
7+
expect(result).to.deep.equal({});
8+
});
9+
10+
it('should extract multiple properties from array of objects', () => {
11+
const items = [
12+
{ id: 1, name: 'John', age: 25, city: 'NY' },
13+
{ id: 2, name: 'Jane', age: 30, city: 'LA' },
14+
];
15+
const result = transposeObjectArray(items, ['id', 'name']);
16+
expect(result).to.deep.equal({
17+
id: [1, 2],
18+
name: ['John', 'Jane'],
19+
rest: [
20+
{ age: 25, city: 'NY' },
21+
{ age: 30, city: 'LA' },
22+
],
23+
});
24+
});
25+
26+
it('should handle objects with missing properties', () => {
27+
const items = [
28+
{ id: 1, name: 'John', age: 25 },
29+
{ id: 2, age: 30 },
30+
{ id: 3, name: 'Bob', city: 'LA' },
31+
];
32+
const result = transposeObjectArray(items, ['id', 'name']);
33+
expect(result).to.deep.equal({
34+
id: [1, 2, 3],
35+
name: ['John', undefined, 'Bob'],
36+
rest: [{ age: 25 }, { age: 30 }, { city: 'LA' }],
37+
});
38+
});
39+
40+
it('should handle different value types', () => {
41+
const items = [
42+
{ id: 1, active: true, count: 10, tags: ['a', 'b'] },
43+
{ id: 2, active: false, count: 20, tags: ['c'] },
44+
];
45+
const result = transposeObjectArray(items, ['active', 'tags']);
46+
expect(result).to.deep.equal({
47+
active: [true, false],
48+
tags: [['a', 'b'], ['c']],
49+
rest: [
50+
{ id: 1, count: 10 },
51+
{ id: 2, count: 20 },
52+
],
53+
});
54+
});
55+
56+
it('should handle extracting all properties (empty rest)', () => {
57+
const items = [
58+
{ id: 1, name: 'John' },
59+
{ id: 2, name: 'Jane' },
60+
];
61+
const result = transposeObjectArray(items, ['id', 'name']);
62+
expect(result).to.deep.equal({
63+
id: [1, 2],
64+
name: ['John', 'Jane'],
65+
rest: [{}, {}],
66+
});
67+
});
68+
69+
it('should handle extracting no properties (everything in rest)', () => {
70+
const items = [
71+
{ id: 1, name: 'John' },
72+
{ id: 2, name: 'Jane' },
73+
];
74+
const result = transposeObjectArray(items, []);
75+
expect(result).to.deep.equal({
76+
rest: [
77+
{ id: 1, name: 'John' },
78+
{ id: 2, name: 'Jane' },
79+
],
80+
});
81+
});
82+
83+
it('should handle objects with null or undefined values', () => {
84+
const items = [
85+
{ id: 1, name: null, age: 25 },
86+
{ id: 2, name: undefined, age: 30 },
87+
];
88+
const result = transposeObjectArray(items, ['id', 'name']);
89+
expect(result).to.deep.equal({
90+
id: [1, 2],
91+
name: [null, undefined],
92+
rest: [{ age: 25 }, { age: 30 }],
93+
});
94+
});
95+
96+
it('should handle nested objects', () => {
97+
const items = [
98+
{ id: 1, user: { name: 'John', age: 25 } },
99+
{ id: 2, user: { name: 'Jane', age: 30 } },
100+
];
101+
const result = transposeObjectArray(items, ['id', 'user']);
102+
expect(result).to.deep.equal({
103+
id: [1, 2],
104+
user: [
105+
{ name: 'John', age: 25 },
106+
{ name: 'Jane', age: 30 },
107+
],
108+
rest: [{}, {}],
109+
});
110+
});
111+
112+
it('should preserve property order in rest object', () => {
113+
const items = [
114+
{ a: 1, b: 2, c: 3, d: 4 },
115+
{ a: 5, b: 6, c: 7, d: 8 },
116+
];
117+
const result = transposeObjectArray(items, ['a', 'c']);
118+
expect(result).to.deep.equal({
119+
a: [1, 5],
120+
c: [3, 7],
121+
rest: [
122+
{ b: 2, d: 4 },
123+
{ b: 6, d: 8 },
124+
],
125+
});
126+
});
127+
});

src/transposeObjectArray.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// eslint-disable-next-line eslint-comments/disable-enable-pair
2+
/* eslint-disable @typescript-eslint/no-explicit-any */
3+
4+
/**
5+
* Type representing a transposed array of objects.
6+
*
7+
* For each selected property K from the original object type T:
8+
* - Creates an array containing all values of that property
9+
* - The array type matches the property's original type
10+
* - Includes a 'rest' array containing all non-selected properties
11+
* @template T - The source object type
12+
* @template K - The keys to transpose into arrays
13+
* @example
14+
* // Original array of objects:
15+
* const users = [
16+
* { id: 1, name: 'John', age: 25, city: 'NY' },
17+
* { id: 2, name: 'Jane', age: 30, city: 'LA' }
18+
* ];
19+
*
20+
* // Type for transposing 'id' and 'name':
21+
* type Result = TransposedObjectArray<typeof users[0], 'id' | 'name'>;
22+
*
23+
* // Resulting type structure:
24+
* {
25+
* id: number[]; // Array of all IDs: [1, 2]
26+
* name: string[]; // Array of all names: ['John', 'Jane']
27+
* rest: Array<{ // Array of remaining properties
28+
* age: number; // [{ age: 25, city: 'NY' },
29+
* city: string; // { age: 30, city: 'LA' }]
30+
* }>;
31+
* }
32+
*/
33+
type TransposedObjectArray<T, K extends keyof T> = {
34+
[P in K]: Array<T[P]>;
35+
} & {
36+
/** Properties not selected for transposition */
37+
rest: Array<Omit<T, K>>;
38+
};
39+
40+
/**
41+
* Transposes an array of objects by converting selected properties into arrays
42+
* while keeping the remaining properties grouped in a 'rest' array.
43+
* @template T - The type of objects in the input array
44+
* @template K - The keys of properties to transpose
45+
* @param items - Array of objects to transpose
46+
* @param properties - Array of property keys to transpose into arrays
47+
* @returns An object containing transposed arrays for each selected property
48+
* @example
49+
* const items = [
50+
* { id: 1, name: 'John', age: 25 },
51+
* { id: 2, name: 'Jane', age: 30 }
52+
* ]
53+
* const result = transposeObjectArray(items, ['id', 'name']);
54+
* // Returns: {
55+
* // id: [1, 2],
56+
* // name: ['John', 'Jane'],
57+
* // rest: [{age: 25}, {age: 30}]
58+
* // }
59+
*/
60+
export const transposeObjectArray = <T extends object, K extends keyof T>(
61+
items: T[],
62+
properties: K[],
63+
): TransposedObjectArray<T, K> =>
64+
items.reduce(
65+
(acc, item) => {
66+
const result = { ...acc } as TransposedObjectArray<T, K>;
67+
68+
properties.forEach((prop) => {
69+
const currentArray = (acc[prop] || []) as T[K][];
70+
result[prop] = [...currentArray, item[prop]] as any;
71+
});
72+
73+
const restObject = {} as Omit<T, K>;
74+
Object.entries(item).forEach(([key, value]) => {
75+
if (!properties.includes(key as K)) {
76+
(restObject as any)[key] = value;
77+
}
78+
});
79+
80+
result.rest = [...(acc.rest || []), restObject];
81+
82+
return result;
83+
},
84+
{} as TransposedObjectArray<T, K>,
85+
);

0 commit comments

Comments
 (0)