Skip to content

Commit 445be47

Browse files
create function partitionObjectArray
1 parent 3b91c10 commit 445be47

File tree

6 files changed

+223
-4
lines changed

6 files changed

+223
-4
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 './partitionObjectArray';

src/partitionObjectArray.ts

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

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({});

src/tests/flattenObject.test.ts

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

0 commit comments

Comments
 (0)