An API returns a nested data structure of arrays and objects. The data es as a list of tree like objects, each with a possible parent-child relationship. The structure itself is shown by the following example code.
[{
label: "search me",
value: "searchme",
children: [{
label: "search me too",
value: "searchmetoo",
children: [{
label: "No one can get me",
value: "anonymous",
}],
}],
}, {
label: "search me2",
value: "searchme2",
children: [{
label: "search me too2",
value: "searchmetoo2",
children: [{
label: "No one can get me2",
value: "anonymous2",
}],
}],
}]
The above data has to be converted into a (flat) array of objects where each object will represent a former node element but with a unique primary key (id). Also a node's parent-id equals its parent's id (the primary key) except for root nodes that do not have a parent, and therefore the parent-id then should be null.
The target structure of the above provided source data then matches the following code ...
[{
id: 1, // DIAGID
parentId: null, // PARENTID
label: "search me", // DIAGNOSIS
value: "searchme" // DIAGTYPE
}, {
id: 2,
parentId: 1,
label: "search me too",
value: "searchmetoo"
}, {
id: 3,
parentId: 2,
label: "No one can get me",
value: "anonymous"
}, {
id: 4,
parentId: null,
label: "search me2",
value: "searchme2"
}, {
id: 5,
parentId: 4,
label: "search me too2",
value: "searchmetoo2"
}, {
id: 6,
parentId: 5,
label: "No one can get me2",
value: "anonymous2"
}]
An API returns a nested data structure of arrays and objects. The data es as a list of tree like objects, each with a possible parent-child relationship. The structure itself is shown by the following example code.
[{
label: "search me",
value: "searchme",
children: [{
label: "search me too",
value: "searchmetoo",
children: [{
label: "No one can get me",
value: "anonymous",
}],
}],
}, {
label: "search me2",
value: "searchme2",
children: [{
label: "search me too2",
value: "searchmetoo2",
children: [{
label: "No one can get me2",
value: "anonymous2",
}],
}],
}]
The above data has to be converted into a (flat) array of objects where each object will represent a former node element but with a unique primary key (id). Also a node's parent-id equals its parent's id (the primary key) except for root nodes that do not have a parent, and therefore the parent-id then should be null.
The target structure of the above provided source data then matches the following code ...
[{
id: 1, // DIAGID
parentId: null, // PARENTID
label: "search me", // DIAGNOSIS
value: "searchme" // DIAGTYPE
}, {
id: 2,
parentId: 1,
label: "search me too",
value: "searchmetoo"
}, {
id: 3,
parentId: 2,
label: "No one can get me",
value: "anonymous"
}, {
id: 4,
parentId: null,
label: "search me2",
value: "searchme2"
}, {
id: 5,
parentId: 4,
label: "search me too2",
value: "searchmetoo2"
}, {
id: 6,
parentId: 5,
label: "No one can get me2",
value: "anonymous2"
}]
Share
edited Nov 5, 2021 at 8:22
Peter Seliger
13.5k3 gold badges30 silver badges44 bronze badges
asked Nov 4, 2021 at 19:15
SunnyCaveSunnyCave
435 bronze badges
7
- Try using JSON.parse on the json data, you may get the desired result. – Ankan Bag Commented Nov 4, 2021 at 19:20
- 1 That is not JSON. – trincot Commented Nov 4, 2021 at 19:23
- 1 The conversion you speak of seems to do a lot more: none of the properties match with anything in the input. Please provide a consistent pair of input/output – trincot Commented Nov 4, 2021 at 19:25
- 1 JSON does not have single quoted string literals. You'll have to update again. But more importantly, if you really get JSON, you'll have to parse it. My assumption is that you get an object, not JSON. Better rephrase that. Also your title says you want JSON as output? Are you sure? I guess you want a JavaScript array, not JSON (which is a text format). – trincot Commented Nov 4, 2021 at 19:27
- 1 please add consistent data of source and target data. what have you tried and what does not work? – Nina Scholz Commented Nov 5, 2021 at 7:22
5 Answers
Reset to default 3You could take a recursive method for Array#flatMap
and store parent
for the next call.
This approach increments id
for all nodes.
const
flatTree = (id => parent => ({ children = [], ...object }) => [
{ id: ++id, ...object, parent },
...children.flatMap(flatTree(id))
])(0),
tree = [{ label: 'search me', value: 'searchme', children: [{ label: 'search me too', value: 'searchmetoo', children: [{ label: 'No one can get me', value: 'anonymous' }] }] }, { label: 'four', searchme: '4four' }],
flat = tree.flatMap(flatTree(null));
console.log(flat);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I was going to say a recursive tree walk is all you need, but you can acplish the same thing easily with a generator:
function *visitNodes( root, parent = null, id = 0 ) {
const node = {
...root,
id : ++id,
parentId = parent ? parent.id : null
};
delete node.children;
yield node;
for ( const child of root.children ?? [] ) {
yield *visitNodes(child, node, id);
}
}
Having defined the generator, you can either iterate over the nodes:
for (const node of visitNodes( tree ) ) {
// do something useful with node here
}
You can convert it into a list easily, either with the spread operator:
const nodes = [...visitNodes(tree)];
or by using Array.from()
:
const nodes = Array.from( visitNodes(tree) );
This version is something of a mash-up of ideas from trincot and Nicholas Carey. From trincot's answer (which actually answers a different, but closely related question) I steal his {ctr: id: 1}
handling. And from Nicholas, I use the generator to iterate through the values more easily, although I simplify a bit from there.
I think they bine to give a nice solution:
const flatten = function * (xs, ctr = {id: 1}, parent = null) {
for (let {children = [], ...x} of xs) {
const node = {id: ctr .id ++, parentId: parent ? parent .id : null, ...x}
yield node
yield * flatten (children, ctr, node)
}
}
const transform = (xs) => [...flatten (xs)]
const response = [{label: "search me", value: "searchme", children: [{label: "search me too", value: "searchmetoo", children: [{label: "No one can get me", value: "anonymous"}]}]}, {label: "search me2", value: "searchme2", children: [{label: "search me too2", value: "searchmetoo2", children: [{label: "No one can get me2", value: "anonymous2"}]}]}]
console .log (transform (response))
.as-console-wrapper {max-height: 100% !important; top: 0}
A single recursively implemented reduce
er functionality which maps and collects any item generically does the job.
It utilizes a collector
object as the reduce
method's 2nd argument (and the reducer's initial value). The collector
's result
array collects any item. And count
gets incremented constantly and assigned as a collected item's id
(former DIAGID
) whereas an item's parentId
(former PARENTID
) gets updated as needed in order to always reflect the current recursive call stack ...
function countMapAndCollectNestedItemRecursively(collector, item) {
let { count = 0, parentId = null, result } = collector;
const { children = [], ...itemRest } = item;
result.push({
id: ++count,
parentId,
...itemRest,
});
count = children.reduce(
countMapAndCollectNestedItemRecursively,
{ count, parentId: count, result }
).count;
return { count, parentId, result };
}
const sampleData = [{
label: "FOO",
value: "foo",
children: [{
label: "FOO BAR",
value: "fooBar",
children: [{
label: "FOO BAR BAZ",
value: "fooBarBaz",
}],
}, {
label: "FOO BIZ",
value: "fooBiz",
children: [{
label: "FOO BIZ BUZ",
value: "fooBizBuz",
}],
}],
}, {
label: "BAR",
value: "bar",
children: [{
label: "BAR BAZ",
value: "barBaz",
children: [{
label: "BAR BAZ BIZ",
value: "barBazBiz",
children: [{
label: "BAR BAZ BIZ BUZ",
value: "barBazBizBuz",
}],
}, {
label: "BAR BAZ BUZ",
value: "barBazBuz",
}],
}, {
label: "BAR BIZ",
value: "barBiz",
children: [{
label: "BAR BIZ BUZ",
value: "barBizBuz",
}],
}],
}];
console.log(
sampleData.reduce(
countMapAndCollectNestedItemRecursively,
{ result: [] },
).result
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Here is a recursive function dfs
that performs a pre-order traversal through the input tree, and passes along a counter that feeds the id
property that will be used in the output. Also the current node's id
is passed as parentId
to the recursive call:
const dfs = (children, counter={id: 1}, parentId=null) =>
children.flatMap(({children=[], ...node}) => [{
...counter,
parentId,
...node
}].concat(dfs(children, counter, counter.id++)));
const response = [{label: "search me",value: "searchme",children: [{label: "search me too",value: "searchmetoo",children: [{label: "No one can get me",value: "anonymous",}],}],}, {label: "search me2",value: "searchme2",children: [{label: "search me too2",value: "searchmetoo2",children: [{label: "No one can get me2",value: "anonymous2",}],}],}];
const result = dfs(response);
console.log(result);
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744911209a4600562.html
评论列表(0条)