Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/popular-falcons-fly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@jhecht/arktype-utils": patch
---

Fixes bug not grouping items correctly
14 changes: 7 additions & 7 deletions packages/arktype-utils/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@
```ts
// Test nested objects with dot notation
const fd = new FormData();
fd.append("payments.id1.location", "England");
fd.append("payments.id1.age", "37");
fd.append("payments.id2.location", "New York");
fd.append('payments.id1.location', 'England');
fd.append('payments.id1.age', '37');
fd.append('payments.id2.location', 'New York');
const obj = formDataToObject(fd);

// Test nested arrays with bracket notation
const fd2 = new FormData();
fd2.append("locations[].name", "England");
fd2.append("locations[].members[]", "John");
fd2.append("locations[].members[]", "Joe");
fd2.append("locations[].name", "France");
fd2.append('locations[].name', 'England');
fd2.append('locations[].members[]', 'John');
fd2.append('locations[].members[]', 'Joe');
fd2.append('locations[].name', 'France');
const obj2 = formDataToObject(fd2);
```

Expand Down
17 changes: 17 additions & 0 deletions packages/arktype-utils/src/formData.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,23 @@ describe('formDataToObject', () => {
const obj = formDataToObject(fd);
expect(obj.file).toBeInstanceOf(File);
});

it('Should work with complex arrays / object combinations', () => {
const fd = new FormData();
fd.append('bills[].id', 'id1');
fd.append('bills[].name', 'Rent');
fd.append('bills[].id', 'id2');
fd.append('bills[].name', 'Electricity');

const obj = formDataToObject(fd);

expect(obj).toStrictEqual({
bills: [
{ id: 'id1', name: 'Rent' },
{ id: 'id2', name: 'Electricity' },
],
});
});
});

describe('formDataToObject - nested keys', () => {
Expand Down
24 changes: 19 additions & 5 deletions packages/arktype-utils/src/formData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,11 @@ export function formDataToObject(
// For grouping array objects by their base path
const arrayGroups = new Map<
string,
{ current: Record<string, unknown>; arr: Record<string, unknown>[] }
{
current: Record<string, unknown>;
arr: Record<string, unknown>[];
lastField?: string;
}
>();
// For collecting repeated simple keys and top-level arrays
const simpleArrays = new Map<string, unknown[]>();
Expand All @@ -191,13 +195,23 @@ export function formDataToObject(
const arrayIdx = path.findIndex((p) => p === '');
if (arrayIdx !== -1) {
const basePath = path.slice(0, arrayIdx).join('.');
if (!arrayGroups.has(basePath))
arrayGroups.set(basePath, { current: {}, arr: [] });
if (!arrayGroups.has(basePath)) {
arrayGroups.set(basePath, {
current: {},
arr: [],
lastField: undefined,
});
}
const group = arrayGroups.get(basePath)!;
// If this is a new object in the array, push current and start new
const thisField = String(path[arrayIdx + 1]);
// Only push current object when a non-array field repeats
const isArrayField =
(path.length === arrayIdx + 3 && path[arrayIdx + 2] === '') ||
String(path[arrayIdx + 1]).endsWith('[]');
if (
Object.keys(group.current).length > 0 &&
path[arrayIdx + 1] === 'name'
Object.prototype.hasOwnProperty.call(group.current, thisField) &&
!isArrayField
) {
group.arr.push(group.current);
group.current = {};
Expand Down
Loading