objwalk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +192 -0
- package/dist/index.cjs +235 -0
- package/dist/index.d.cts +17 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +204 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# objwalk
|
|
2
|
+
|
|
3
|
+
Small utilities for reading, writing, walking, and reshaping nested JavaScript objects.
|
|
4
|
+
|
|
5
|
+
`objwalk` is built around a compact path syntax, so the same mental model works for picking values, setting values, extracting a new object shape, and walking through nested data.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install objwalk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { each, extract, map, pick, set } from 'objwalk'
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## pick
|
|
20
|
+
|
|
21
|
+
Read a value from an object by path.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
const data = {
|
|
25
|
+
user: {
|
|
26
|
+
name: 'Natalia Romanoff',
|
|
27
|
+
email: 'black.widow@example.com',
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
pick(data, 'user.name')
|
|
32
|
+
// 'Natalia Romanoff'
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Pick several keys from the same nested object.
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
pick(data, 'user.{name,email}')
|
|
39
|
+
// { name: 'Natalia Romanoff', email: 'black.widow@example.com' }
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
List keys from a nested object.
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
pick(data, 'user.{*}')
|
|
46
|
+
// ['name', 'email']
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Read from arrays with an explicit index.
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
const data = {
|
|
53
|
+
items: [{ title: 'First' }, { title: 'Second' }],
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
pick(data, 'items[1].title')
|
|
57
|
+
// 'Second'
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
If the path cannot be resolved, `pick` returns `undefined`.
|
|
61
|
+
|
|
62
|
+
## set
|
|
63
|
+
|
|
64
|
+
Set a value inside an object by path.
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
const data = {}
|
|
68
|
+
|
|
69
|
+
set(data, 'user.name', 'Natalia')
|
|
70
|
+
// { user: { name: 'Natalia' } }
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Set values inside arrays with an explicit index.
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
const data = {}
|
|
77
|
+
|
|
78
|
+
set(data, 'items[0].title', 'First')
|
|
79
|
+
// { items: [{ title: 'Romanoff' }] }
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`set` mutates the object passed to it and returns the same object.
|
|
83
|
+
|
|
84
|
+
## extract
|
|
85
|
+
|
|
86
|
+
Create a new object by mapping destination paths to source paths.
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
const source = {
|
|
90
|
+
profile: {
|
|
91
|
+
fullName: 'Natalia Romanoff',
|
|
92
|
+
email: 'black.widow@example.com',
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
extract(source, {
|
|
97
|
+
'user.name': 'profile.fullName',
|
|
98
|
+
'user.email': 'profile.email',
|
|
99
|
+
})
|
|
100
|
+
// {
|
|
101
|
+
// user: {
|
|
102
|
+
// name: 'Natalia Romanoff',
|
|
103
|
+
// email: 'black.widow@example.com',
|
|
104
|
+
// },
|
|
105
|
+
// }
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Use an array of source paths to provide fallbacks. The first resolved value is used.
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
extract(source, {
|
|
112
|
+
'user.name': ['profile.displayName', 'profile.fullName'],
|
|
113
|
+
})
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Use `[n]` to map over arrays.
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
const source = {
|
|
120
|
+
products: [
|
|
121
|
+
{ name: 'Keyboard' },
|
|
122
|
+
{ name: 'Mouse' },
|
|
123
|
+
],
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
extract(source, {
|
|
127
|
+
'items[n].title': 'products[n].name',
|
|
128
|
+
})
|
|
129
|
+
// {
|
|
130
|
+
// items: [
|
|
131
|
+
// { title: 'Keyboard' },
|
|
132
|
+
// { title: 'Mouse' },
|
|
133
|
+
// ],
|
|
134
|
+
// }
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## each
|
|
138
|
+
|
|
139
|
+
Walk through an object and call a function for each value.
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
each(data, (key, value, path) => {
|
|
143
|
+
console.log(key, value, path)
|
|
144
|
+
})
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The callback receives:
|
|
148
|
+
|
|
149
|
+
- `key`: the current object key or array index
|
|
150
|
+
- `value`: the current value
|
|
151
|
+
- `path`: the full path to the current value
|
|
152
|
+
|
|
153
|
+
## map
|
|
154
|
+
|
|
155
|
+
Walk through an object and build a new result.
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
const result = map(data, (key, value, store, path) => {
|
|
159
|
+
if (typeof value === 'string') {
|
|
160
|
+
set(store, path, value.trim())
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
The callback receives:
|
|
166
|
+
|
|
167
|
+
- `key`: the current object key or array index
|
|
168
|
+
- `value`: the current value
|
|
169
|
+
- `store`: the result object being built
|
|
170
|
+
- `path`: the full path to the current value
|
|
171
|
+
|
|
172
|
+
`map` returns `store`.
|
|
173
|
+
|
|
174
|
+
## Path syntax
|
|
175
|
+
|
|
176
|
+
| Syntax | Description |
|
|
177
|
+
| --- | --- |
|
|
178
|
+
| `user.name` | Read or write a nested property. |
|
|
179
|
+
| `items[0].title` | Read or write an array item by index. |
|
|
180
|
+
| `user.{name,email}` | Pick several keys from one object. |
|
|
181
|
+
| `user.{*}` | List keys from one object. |
|
|
182
|
+
| `items[n].title` | Map every item in an array during `extract`. |
|
|
183
|
+
|
|
184
|
+
## API
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
pick(data, path)
|
|
188
|
+
set(data, path, value)
|
|
189
|
+
extract(data, mapping)
|
|
190
|
+
each(data, callback)
|
|
191
|
+
map(data, callback)
|
|
192
|
+
```
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
each: () => each,
|
|
24
|
+
extract: () => extract,
|
|
25
|
+
map: () => map,
|
|
26
|
+
pick: () => pick,
|
|
27
|
+
set: () => set
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(index_exports);
|
|
30
|
+
|
|
31
|
+
// src/each.ts
|
|
32
|
+
var isWalkable = (value) => {
|
|
33
|
+
return value !== null && typeof value === "object";
|
|
34
|
+
};
|
|
35
|
+
var createPath = (parentPath, key, parentIsArray) => {
|
|
36
|
+
if (parentIsArray) {
|
|
37
|
+
return parentPath + "[" + key + "]";
|
|
38
|
+
}
|
|
39
|
+
return parentPath ? parentPath + "." + key : String(key);
|
|
40
|
+
};
|
|
41
|
+
var each = (data, callback) => {
|
|
42
|
+
const walk = (value, path, key, parentIsArray) => {
|
|
43
|
+
const currentPath = path || createPath("", key, parentIsArray);
|
|
44
|
+
callback(key, value, currentPath);
|
|
45
|
+
if (!isWalkable(value)) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (Array.isArray(value)) {
|
|
49
|
+
value.forEach((item, index) => {
|
|
50
|
+
walk(item, createPath(currentPath, index, true), index, true);
|
|
51
|
+
});
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
Object.entries(value).forEach(([childKey, item]) => {
|
|
55
|
+
walk(item, createPath(currentPath, childKey, false), childKey, false);
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
if (!isWalkable(data)) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (Array.isArray(data)) {
|
|
62
|
+
data.forEach((item, index) => {
|
|
63
|
+
walk(item, createPath("", index, true), index, true);
|
|
64
|
+
});
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
68
|
+
walk(value, createPath("", key, false), key, false);
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// src/helpers/getValueByPath.ts
|
|
73
|
+
var getValueByPath = (obj, path) => {
|
|
74
|
+
if (!path) return obj;
|
|
75
|
+
return path.split(".").reduce((result, current) => {
|
|
76
|
+
if (result !== null && result !== void 0) {
|
|
77
|
+
const match = current.match(/(w+)[(d+)]/);
|
|
78
|
+
if (match) {
|
|
79
|
+
const [, key, index] = match;
|
|
80
|
+
const array = result[key];
|
|
81
|
+
return array && Array.isArray(array) ? array[parseInt(index, 10)] : void 0;
|
|
82
|
+
}
|
|
83
|
+
return result[current];
|
|
84
|
+
}
|
|
85
|
+
return void 0;
|
|
86
|
+
}, obj);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// src/helpers/parsePathSelection.ts
|
|
90
|
+
var selectionRegex = /(.*){([^}]+)}$/;
|
|
91
|
+
var normalizeSelectionPath = (path) => {
|
|
92
|
+
return path.endsWith(".") ? path.slice(0, -1) : path;
|
|
93
|
+
};
|
|
94
|
+
var parseSelectedKeys = (selection) => {
|
|
95
|
+
if (selection.trim() === "*") {
|
|
96
|
+
return "all";
|
|
97
|
+
}
|
|
98
|
+
return selection.split(",").map((key) => key.trim()).filter(Boolean);
|
|
99
|
+
};
|
|
100
|
+
var parsePathSelection = (path = "") => {
|
|
101
|
+
const matches = path.match(selectionRegex);
|
|
102
|
+
if (!matches) {
|
|
103
|
+
return {
|
|
104
|
+
cleanPath: path,
|
|
105
|
+
keys: []
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
cleanPath: normalizeSelectionPath(matches[1]),
|
|
110
|
+
keys: parseSelectedKeys(matches[2])
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// src/pick.ts
|
|
115
|
+
var pick = (data = {}, path = "") => {
|
|
116
|
+
const { cleanPath, keys } = parsePathSelection(path);
|
|
117
|
+
const value = getValueByPath(data, cleanPath);
|
|
118
|
+
if (keys.length === 0 && keys !== "all") {
|
|
119
|
+
return value;
|
|
120
|
+
}
|
|
121
|
+
if (keys === "all") {
|
|
122
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
123
|
+
return Object.keys(value);
|
|
124
|
+
}
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
if (value && typeof value === "object") {
|
|
128
|
+
if (keys.length === 1) {
|
|
129
|
+
return value[keys[0]];
|
|
130
|
+
}
|
|
131
|
+
return keys.reduce(
|
|
132
|
+
(result, key) => ({
|
|
133
|
+
...result,
|
|
134
|
+
[key]: value[key]
|
|
135
|
+
}),
|
|
136
|
+
{}
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
return void 0;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// src/set.ts
|
|
143
|
+
var parseArrayPathSegment = (key) => {
|
|
144
|
+
const matches = key.match(/^(\w+)\[(\d+)\]$/);
|
|
145
|
+
if (matches) {
|
|
146
|
+
return {
|
|
147
|
+
key: matches[1],
|
|
148
|
+
isArray: true,
|
|
149
|
+
index: parseInt(matches[2], 10)
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
return { key, isArray: false, index: -1 };
|
|
153
|
+
};
|
|
154
|
+
var set = (data = {}, path, value) => {
|
|
155
|
+
const keys = path.split(".");
|
|
156
|
+
let current = data;
|
|
157
|
+
for (let i = 0; i < keys.length; i++) {
|
|
158
|
+
const { key, isArray, index } = parseArrayPathSegment(keys[i]);
|
|
159
|
+
const isLastKey = i === keys.length - 1;
|
|
160
|
+
if (isArray) {
|
|
161
|
+
if (!current[key] || !Array.isArray(current[key])) {
|
|
162
|
+
current[key] = [];
|
|
163
|
+
}
|
|
164
|
+
if (index >= current[key].length) {
|
|
165
|
+
current[key].length = index + 1;
|
|
166
|
+
}
|
|
167
|
+
if (!isLastKey && current[key][index] === void 0) {
|
|
168
|
+
current[key][index] = {};
|
|
169
|
+
}
|
|
170
|
+
if (isLastKey) {
|
|
171
|
+
current[key][index] = value;
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
current = current[key][index];
|
|
175
|
+
} else if (!isLastKey) {
|
|
176
|
+
if (!current[key] || typeof current[key] !== "object") {
|
|
177
|
+
current[key] = {};
|
|
178
|
+
}
|
|
179
|
+
current = current[key];
|
|
180
|
+
} else {
|
|
181
|
+
current[key] = value;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return data;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// src/extract.ts
|
|
188
|
+
var mapArrayItems = (destinationPath, sourcePath, source = {}, result = {}) => {
|
|
189
|
+
const destinationParts = destinationPath.split("[n].");
|
|
190
|
+
const sourceParts = sourcePath.split("[n].");
|
|
191
|
+
const array = pick(source, sourceParts[0]);
|
|
192
|
+
if (Array.isArray(array)) {
|
|
193
|
+
array.forEach((row, index) => {
|
|
194
|
+
const itemPath = destinationParts[0] + "[" + index + "]." + destinationParts[1];
|
|
195
|
+
set(result, itemPath, pick(row, sourceParts[1]));
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
var extract = (source, mapping) => {
|
|
200
|
+
const result = {};
|
|
201
|
+
Object.entries(mapping).forEach(([destinationPath, sourcePath]) => {
|
|
202
|
+
if (destinationPath.includes("[n]") && typeof sourcePath === "string") {
|
|
203
|
+
mapArrayItems(destinationPath, sourcePath, source, result);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (Array.isArray(sourcePath)) {
|
|
207
|
+
for (const item of sourcePath) {
|
|
208
|
+
const value = pick(source, item);
|
|
209
|
+
if (value !== void 0) {
|
|
210
|
+
set(result, destinationPath, value);
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
set(result, destinationPath, pick(source, sourcePath));
|
|
217
|
+
});
|
|
218
|
+
return result;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
// src/map.ts
|
|
222
|
+
var map = (data, callback, store = {}) => {
|
|
223
|
+
each(data, (key, value, path) => {
|
|
224
|
+
callback(key, value, store, path);
|
|
225
|
+
});
|
|
226
|
+
return store;
|
|
227
|
+
};
|
|
228
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
229
|
+
0 && (module.exports = {
|
|
230
|
+
each,
|
|
231
|
+
extract,
|
|
232
|
+
map,
|
|
233
|
+
pick,
|
|
234
|
+
set
|
|
235
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
type WalkKey = string | number;
|
|
2
|
+
type EachCallback = (key: WalkKey, value: any, path: string) => void;
|
|
3
|
+
declare const each: (data: any, callback: EachCallback) => void;
|
|
4
|
+
|
|
5
|
+
type ObjwalkObject$1 = Record<string, any>;
|
|
6
|
+
declare const pick: (data?: ObjwalkObject$1, path?: string) => any;
|
|
7
|
+
|
|
8
|
+
type ExtractMapping = Record<string, string | string[]>;
|
|
9
|
+
declare const extract: (source: ObjwalkObject$1, mapping: ExtractMapping) => ObjwalkObject$1;
|
|
10
|
+
|
|
11
|
+
type MapCallback<TStore extends Record<string, any> = Record<string, any>> = (key: WalkKey, value: any, store: TStore, path: string) => void;
|
|
12
|
+
declare const map: <TStore extends Record<string, any> = Record<string, any>>(data: any, callback: MapCallback<TStore>, store?: TStore) => TStore;
|
|
13
|
+
|
|
14
|
+
type ObjwalkObject = Record<string, any>;
|
|
15
|
+
declare const set: (data: ObjwalkObject | undefined, path: string, value: any) => ObjwalkObject;
|
|
16
|
+
|
|
17
|
+
export { type EachCallback, type ExtractMapping, type MapCallback, type ObjwalkObject$1 as ObjwalkObject, type WalkKey, each, extract, map, pick, set };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
type WalkKey = string | number;
|
|
2
|
+
type EachCallback = (key: WalkKey, value: any, path: string) => void;
|
|
3
|
+
declare const each: (data: any, callback: EachCallback) => void;
|
|
4
|
+
|
|
5
|
+
type ObjwalkObject$1 = Record<string, any>;
|
|
6
|
+
declare const pick: (data?: ObjwalkObject$1, path?: string) => any;
|
|
7
|
+
|
|
8
|
+
type ExtractMapping = Record<string, string | string[]>;
|
|
9
|
+
declare const extract: (source: ObjwalkObject$1, mapping: ExtractMapping) => ObjwalkObject$1;
|
|
10
|
+
|
|
11
|
+
type MapCallback<TStore extends Record<string, any> = Record<string, any>> = (key: WalkKey, value: any, store: TStore, path: string) => void;
|
|
12
|
+
declare const map: <TStore extends Record<string, any> = Record<string, any>>(data: any, callback: MapCallback<TStore>, store?: TStore) => TStore;
|
|
13
|
+
|
|
14
|
+
type ObjwalkObject = Record<string, any>;
|
|
15
|
+
declare const set: (data: ObjwalkObject | undefined, path: string, value: any) => ObjwalkObject;
|
|
16
|
+
|
|
17
|
+
export { type EachCallback, type ExtractMapping, type MapCallback, type ObjwalkObject$1 as ObjwalkObject, type WalkKey, each, extract, map, pick, set };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// src/each.ts
|
|
2
|
+
var isWalkable = (value) => {
|
|
3
|
+
return value !== null && typeof value === "object";
|
|
4
|
+
};
|
|
5
|
+
var createPath = (parentPath, key, parentIsArray) => {
|
|
6
|
+
if (parentIsArray) {
|
|
7
|
+
return parentPath + "[" + key + "]";
|
|
8
|
+
}
|
|
9
|
+
return parentPath ? parentPath + "." + key : String(key);
|
|
10
|
+
};
|
|
11
|
+
var each = (data, callback) => {
|
|
12
|
+
const walk = (value, path, key, parentIsArray) => {
|
|
13
|
+
const currentPath = path || createPath("", key, parentIsArray);
|
|
14
|
+
callback(key, value, currentPath);
|
|
15
|
+
if (!isWalkable(value)) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (Array.isArray(value)) {
|
|
19
|
+
value.forEach((item, index) => {
|
|
20
|
+
walk(item, createPath(currentPath, index, true), index, true);
|
|
21
|
+
});
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
Object.entries(value).forEach(([childKey, item]) => {
|
|
25
|
+
walk(item, createPath(currentPath, childKey, false), childKey, false);
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
if (!isWalkable(data)) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (Array.isArray(data)) {
|
|
32
|
+
data.forEach((item, index) => {
|
|
33
|
+
walk(item, createPath("", index, true), index, true);
|
|
34
|
+
});
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
38
|
+
walk(value, createPath("", key, false), key, false);
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// src/helpers/getValueByPath.ts
|
|
43
|
+
var getValueByPath = (obj, path) => {
|
|
44
|
+
if (!path) return obj;
|
|
45
|
+
return path.split(".").reduce((result, current) => {
|
|
46
|
+
if (result !== null && result !== void 0) {
|
|
47
|
+
const match = current.match(/(w+)[(d+)]/);
|
|
48
|
+
if (match) {
|
|
49
|
+
const [, key, index] = match;
|
|
50
|
+
const array = result[key];
|
|
51
|
+
return array && Array.isArray(array) ? array[parseInt(index, 10)] : void 0;
|
|
52
|
+
}
|
|
53
|
+
return result[current];
|
|
54
|
+
}
|
|
55
|
+
return void 0;
|
|
56
|
+
}, obj);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// src/helpers/parsePathSelection.ts
|
|
60
|
+
var selectionRegex = /(.*){([^}]+)}$/;
|
|
61
|
+
var normalizeSelectionPath = (path) => {
|
|
62
|
+
return path.endsWith(".") ? path.slice(0, -1) : path;
|
|
63
|
+
};
|
|
64
|
+
var parseSelectedKeys = (selection) => {
|
|
65
|
+
if (selection.trim() === "*") {
|
|
66
|
+
return "all";
|
|
67
|
+
}
|
|
68
|
+
return selection.split(",").map((key) => key.trim()).filter(Boolean);
|
|
69
|
+
};
|
|
70
|
+
var parsePathSelection = (path = "") => {
|
|
71
|
+
const matches = path.match(selectionRegex);
|
|
72
|
+
if (!matches) {
|
|
73
|
+
return {
|
|
74
|
+
cleanPath: path,
|
|
75
|
+
keys: []
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
cleanPath: normalizeSelectionPath(matches[1]),
|
|
80
|
+
keys: parseSelectedKeys(matches[2])
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// src/pick.ts
|
|
85
|
+
var pick = (data = {}, path = "") => {
|
|
86
|
+
const { cleanPath, keys } = parsePathSelection(path);
|
|
87
|
+
const value = getValueByPath(data, cleanPath);
|
|
88
|
+
if (keys.length === 0 && keys !== "all") {
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
if (keys === "all") {
|
|
92
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
93
|
+
return Object.keys(value);
|
|
94
|
+
}
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
if (value && typeof value === "object") {
|
|
98
|
+
if (keys.length === 1) {
|
|
99
|
+
return value[keys[0]];
|
|
100
|
+
}
|
|
101
|
+
return keys.reduce(
|
|
102
|
+
(result, key) => ({
|
|
103
|
+
...result,
|
|
104
|
+
[key]: value[key]
|
|
105
|
+
}),
|
|
106
|
+
{}
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return void 0;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// src/set.ts
|
|
113
|
+
var parseArrayPathSegment = (key) => {
|
|
114
|
+
const matches = key.match(/^(\w+)\[(\d+)\]$/);
|
|
115
|
+
if (matches) {
|
|
116
|
+
return {
|
|
117
|
+
key: matches[1],
|
|
118
|
+
isArray: true,
|
|
119
|
+
index: parseInt(matches[2], 10)
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return { key, isArray: false, index: -1 };
|
|
123
|
+
};
|
|
124
|
+
var set = (data = {}, path, value) => {
|
|
125
|
+
const keys = path.split(".");
|
|
126
|
+
let current = data;
|
|
127
|
+
for (let i = 0; i < keys.length; i++) {
|
|
128
|
+
const { key, isArray, index } = parseArrayPathSegment(keys[i]);
|
|
129
|
+
const isLastKey = i === keys.length - 1;
|
|
130
|
+
if (isArray) {
|
|
131
|
+
if (!current[key] || !Array.isArray(current[key])) {
|
|
132
|
+
current[key] = [];
|
|
133
|
+
}
|
|
134
|
+
if (index >= current[key].length) {
|
|
135
|
+
current[key].length = index + 1;
|
|
136
|
+
}
|
|
137
|
+
if (!isLastKey && current[key][index] === void 0) {
|
|
138
|
+
current[key][index] = {};
|
|
139
|
+
}
|
|
140
|
+
if (isLastKey) {
|
|
141
|
+
current[key][index] = value;
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
current = current[key][index];
|
|
145
|
+
} else if (!isLastKey) {
|
|
146
|
+
if (!current[key] || typeof current[key] !== "object") {
|
|
147
|
+
current[key] = {};
|
|
148
|
+
}
|
|
149
|
+
current = current[key];
|
|
150
|
+
} else {
|
|
151
|
+
current[key] = value;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return data;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// src/extract.ts
|
|
158
|
+
var mapArrayItems = (destinationPath, sourcePath, source = {}, result = {}) => {
|
|
159
|
+
const destinationParts = destinationPath.split("[n].");
|
|
160
|
+
const sourceParts = sourcePath.split("[n].");
|
|
161
|
+
const array = pick(source, sourceParts[0]);
|
|
162
|
+
if (Array.isArray(array)) {
|
|
163
|
+
array.forEach((row, index) => {
|
|
164
|
+
const itemPath = destinationParts[0] + "[" + index + "]." + destinationParts[1];
|
|
165
|
+
set(result, itemPath, pick(row, sourceParts[1]));
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
var extract = (source, mapping) => {
|
|
170
|
+
const result = {};
|
|
171
|
+
Object.entries(mapping).forEach(([destinationPath, sourcePath]) => {
|
|
172
|
+
if (destinationPath.includes("[n]") && typeof sourcePath === "string") {
|
|
173
|
+
mapArrayItems(destinationPath, sourcePath, source, result);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (Array.isArray(sourcePath)) {
|
|
177
|
+
for (const item of sourcePath) {
|
|
178
|
+
const value = pick(source, item);
|
|
179
|
+
if (value !== void 0) {
|
|
180
|
+
set(result, destinationPath, value);
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
set(result, destinationPath, pick(source, sourcePath));
|
|
187
|
+
});
|
|
188
|
+
return result;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// src/map.ts
|
|
192
|
+
var map = (data, callback, store = {}) => {
|
|
193
|
+
each(data, (key, value, path) => {
|
|
194
|
+
callback(key, value, store, path);
|
|
195
|
+
});
|
|
196
|
+
return store;
|
|
197
|
+
};
|
|
198
|
+
export {
|
|
199
|
+
each,
|
|
200
|
+
extract,
|
|
201
|
+
map,
|
|
202
|
+
pick,
|
|
203
|
+
set
|
|
204
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "objwalk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Small utilities for reading, writing, walking, and reshaping nested JavaScript objects.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"object",
|
|
7
|
+
"path",
|
|
8
|
+
"object-path",
|
|
9
|
+
"pick",
|
|
10
|
+
"set",
|
|
11
|
+
"extract",
|
|
12
|
+
"map",
|
|
13
|
+
"walk",
|
|
14
|
+
"traverse",
|
|
15
|
+
"transform",
|
|
16
|
+
"nested",
|
|
17
|
+
"mapper"
|
|
18
|
+
],
|
|
19
|
+
"license": "ISC",
|
|
20
|
+
"author": "Sergey Khalilov <sergey.khalilov@gmail.com>",
|
|
21
|
+
"type": "module",
|
|
22
|
+
"main": "./dist/index.cjs",
|
|
23
|
+
"module": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.js",
|
|
29
|
+
"require": "./dist/index.cjs"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"README.md"
|
|
35
|
+
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --clean",
|
|
38
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
39
|
+
"pack:check": "npm pack --dry-run"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"tsup": "^8.5.1",
|
|
43
|
+
"typescript": "^5.9.3"
|
|
44
|
+
}
|
|
45
|
+
}
|