Skip to content

Commit 89089aa

Browse files
committed
change: set to default to array for indices in path, introduce object-syntax
1 parent e476ded commit 89089aa

File tree

4 files changed

+105
-10
lines changed

4 files changed

+105
-10
lines changed

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,45 @@ const data = pointer.set({}, ['list', '[]', 'value'], 42);
117117
console.log(data); // output: { list: [ { value: 42 } ] }
118118
```
119119

120+
#### behaviour using `set`
121+
122+
`set` will create arrays when encountering a number
123+
124+
```js
125+
pointer.set({}, ['list', '1', 'value'], 42);
126+
// { list: [undefined, { value: 42 }] }
127+
```
128+
129+
alternatively you may use array-syntax `[index]`
130+
131+
```js
132+
pointer.set({}, ['list', '[1]', 'value'], 42);
133+
// { list: [undefined, { value: 42 }] }
134+
```
135+
136+
append items using empty array syntax `[]`
137+
138+
```js
139+
pointer.set({ list: [1, 2] }, ['list', '[]', 'value'], 42);
140+
// { list: [1, 2, { value: 42 }] }
141+
```
142+
143+
create object using object syntax `{index}`
144+
145+
```js
146+
pointer.set({}, ['list', '{1}', 'value'], 42);
147+
// { list: { 1: { value: 42 } }
148+
```
149+
150+
> ⚠️ `set` prefers existing data-type over specified data-types. For example: Setting an object for an existing array, will keep the object intact:
151+
>
152+
> ```js
153+
> pointer.set({ list: []}, ['list', '{0}', 'value'], 42);
154+
> // { list: [{ value: 42 }] }
155+
> ```
156+
157+
158+
120159
#### array
121160
122161
Per default `set` creates objects for each each missing data. Thus, a pointer to `/list/1` will create an object with `{ list: { 1: {} } }`. If you want to specify array-data you will need to wrap the array-index in array-brackets:
@@ -276,6 +315,9 @@ console.log(pointer); // output: '/my pointer/to/property'
276315

277316
## Breaking Changes
278317

318+
- 2025/01/14 with `v7`
319+
- `pointer.set` creates arrays using numbers as properties `/1` when the data is null or an array (previously created objects)
320+
- `pointer.set` creates objects for numbers when using object-syntax `/{1}` (previously unsupported)
279321
- 2024/04/06 with `v6`, selection of empty properties is supported:
280322
- `"/"` now selects an empty property (previously root-pointer)
281323
- `"a//b"` is now a valid pointer to `"a" » "" » "b"`

lib/set.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { JsonPointer, JsonPath, JsonData } from "./types";
33

44
const isArray = /^\[.*\]$/;
55
const arrayIndex = /^\[(.+)\]$/;
6+
const findProperty = /^[[{](.+)[\]}]$/;
67

78
function accessToPrototype(key: string, properties: string[]) {
89
return (
@@ -36,7 +37,7 @@ export function set<T = JsonData>(
3637
current = data;
3738
while (properties.length > 1) {
3839
key = properties.shift();
39-
nextKeyIsArray = isArray.test(properties[0]);
40+
nextKeyIsArray = isArray.test(properties[0]) || `${parseInt(properties[0])}` === properties[0];
4041
if (accessToPrototype(key, properties)) {
4142
continue;
4243
}
@@ -48,20 +49,16 @@ export function set<T = JsonData>(
4849
}
4950

5051
function addValue(data, key, value) {
51-
let index;
52-
const keyAsIndex = key.match(arrayIndex);
52+
const property = key.match(findProperty)?.pop() ?? key;
5353
if (key === "[]" && Array.isArray(data)) {
5454
data.push(value);
55-
} else if (keyAsIndex) {
56-
index = keyAsIndex.pop();
57-
data[index] = value;
5855
} else {
59-
data[key] = value;
56+
data[property] = value;
6057
}
6158
}
6259

6360
function create(data, key, isArray) {
64-
const property = key.match(arrayIndex)?.pop() ?? key;
61+
const property = key.match(findProperty)?.pop() ?? key;
6562
if (data[property] != null) {
6663
return data[property];
6764
}

test/issues/issue23.set-merge.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,35 @@ describe("pointer.set", () => {
1212

1313
expect(obj.list).to.deep.equal([{ itemA: 1, itemB: 1 }]);
1414
});
15+
16+
it("should merge properties in array items using number as index", () => {
17+
const obj: Record<string, any> = {};
18+
19+
set(obj, '/list/0/itemA', 1)
20+
set(obj, '/list/0/itemB', 1)
21+
22+
expect(obj.list).to.deep.equal([{ itemA: 1, itemB: 1 }]);
23+
});
24+
25+
it("should prefer existing data-type array over specified data-type in pointer", () => {
26+
const obj: Record<string, any> = {
27+
list: []
28+
};
29+
30+
set(obj, '/list/{0}/itemA', 1)
31+
set(obj, '/list/{0}/itemB', 1)
32+
33+
expect(obj.list).to.deep.equal([{ itemA: 1, itemB: 1 }]);
34+
});
35+
36+
it("should prefer existing data-type object over specified data-type in pointer", () => {
37+
const obj: Record<string, any> = {
38+
list: {}
39+
};
40+
41+
set(obj, '/list/[0]/itemA', 1)
42+
set(obj, '/list/[0]/itemB', 1)
43+
44+
expect(obj.list).to.deep.equal({ 0: { itemA: 1, itemB: 1 } });
45+
});
1546
});

test/unit/set.test.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ describe("pointer.set", () => {
7070
expect(result.array.length).to.eq(1);
7171
});
7272

73-
it("should insert index in array", () => {
73+
it("should insert index in array using array-syntax", () => {
7474
const result = set<{ array?: any[] }>({}, "/array/[1]", true);
7575

7676
assert(result.array);
@@ -79,6 +79,23 @@ describe("pointer.set", () => {
7979
expect(result.array[1]).to.be.true;
8080
});
8181

82+
it("should insert index in array using number as string", () => {
83+
const result = set<{ array?: any[] }>({}, "/array/1", true);
84+
85+
assert(result.array);
86+
expect(result.array).to.be.an("array");
87+
expect(result.array.length).to.eq(2);
88+
expect(result.array[1]).to.be.true;
89+
});
90+
91+
it("should insert object using object-syntax", () => {
92+
const result = set<{ array?: any[] }>({}, "/array/{1}", true);
93+
94+
assert(result.array);
95+
expect(result.array).to.be.an("object");
96+
expect(result.array).to.deep.equal({ "1": true });
97+
});
98+
8299
it("should append item to array", () => {
83100
const result = set<{ array: any[] }>({ array: ["first"] }, "/array/[]", "next");
84101

@@ -94,14 +111,22 @@ describe("pointer.set", () => {
94111
expect(result.array[0][0]).to.be.true;
95112
});
96113

97-
it("should insert array to index in array", () => {
114+
it("should insert array to index in array using array-syntax", () => {
98115
const result = set<{ array?: any[] }>({}, "/array/[1]/[]", true);
99116

100117
assert(result.array);
101118
expect(result.array).to.be.an("array");
102119
expect(result.array[1][0]).to.be.true;
103120
});
104121

122+
it("should insert array to index in array using number as string", () => {
123+
const result = set<{ array?: any[] }>({}, "/array/1/[]", true);
124+
125+
assert(result.array);
126+
expect(result.array).to.be.an("array");
127+
expect(result.array[1][0]).to.be.true;
128+
});
129+
105130
it("should insert object in array", () => {
106131
const result = set<{ array?: any[] }>(
107132
{},

0 commit comments

Comments
 (0)