Relative novice to Javascript here. I am having issue wrapping my head around the order of operations I need.
I am trying to create a json-like parent-child object from a 2d adjacency matrix. For theses data I do not need to implement a recursive solution, as there are only parent-child nodes (no grand-parent nodes). Still, to my eye it has a lot of nesting, thus my confusion.
I have an array of objects that represent forward links of a node-link diagram I just care if the link exists, not the weights, it is of the form:
exampleRawLinks = [
{ n1: nan, n2: 1.2, n3: nan, n4: 1.3],
{ n1: nan, n2: nan, n3: nan, n4: 1.4],
{ n1: nan, n2: 1.5, n3: nan, n4: 1.4],
{ n1: nan, n2: nan, n3: 1.3, n4: nan],
]
The column and row indices are symmetric, i.e., (0,0) is the same node, (1,1) is the same node, etc.
Each node belongs to a parent region (numeric) and has a name(string), these data are encoded as columns at the beginning of the adjacency matrix, yielding:
nodeLinks = [
{region: 1, node: "n1", n1: nan, n2: 1.2, n3: nan, n4: 1.3],
{region: 1, node: "n2", n1: nan, n2: nan, n3: nan, n4: 1.4],
{region: 2, node: "n3", n1: nan, n2: 1.5, n3: nan, n4: 1.4],
{region: 3, node: "n4", n1: nan, n2: nan, n3: 1.3, n4: nan],
]
I am trying to achieve the form: (id(numeric) and group(numeric) have identical region information, targets are the true values on each row of adjacency matrix)
dataFinal =
{children:
[
{id: 1,
children:
[
{
id:"n1"
group: 1
targets:["n2","n4"]
},
{
id:"n2"
group: 1
targets:[]
},
]
},
{id: 2,
children:
[
{
id:"n1"
group: 2
targets:["n2"]
}
]
},
{id: 3,
children:
[
{
id:"n1"
group: 3
targets:["n3"]
}
]
}
]
}
I have fiddled around with d3.groups(...)
to handle the region grouping, resulting in a nested array. I suspect a solution is a nested .map
, I just don't quite know how to approach implementing that. Beyond that there may be a .toNestedJson()
method somewhere for adjacency matrices I am just not aware of despite my searching.
Thank you for any and all help.
UPDATE: ok, I am close, just unable to get the targets to map correctly (the below results in an empty array for targets, the slice is acting on too small of an array, so I am missing something):
dataFinal = Object.entries(nodeLinks)
.map(d => ({id: d[1].region, children: [({id: d[1].node, group: d[1].region, targets: Object.keys(Array(d[1]).slice(2,-1).map(obj => _.omitBy(obj, _.isNull)))})] })
);
CodePudding user response:
const nodeLinks = [
{region: 1, node: "n1", n1: NaN, n2: 1.2, n3: NaN, n4: 1.3},
{region: 1, node: "n2", n1: NaN, n2: NaN, n3: NaN, n4: 1.4},
{region: 2, node: "n3", n1: NaN, n2: 1.5, n3: NaN, n4: 1.4},
{region: 3, node: "n4", n1: NaN, n2: NaN, n3: 1.3, n4: NaN},
];
// reshape `nodeLinks` items and map them to their regions and ids
const regions = {};
const nodes = {};
nodeLinks.forEach(node => {
if(!(node.region in regions)) regions[node.region] = [];
const item = {
id: node.node,
group: node.region,
targets: []
};
regions[node.region].push(item);
nodes[node.node] = item;
});
// now we have a complete list of possible target keys so add the targets
const nodeKeys = Object.keys(nodes);
nodeLinks.forEach(node => {
nodes[node.node].targets = nodeKeys
.map(key => (key in node && !Number.isNaN(node[key]) ? key : undefined))
.filter(item => item !== undefined);
});
// build final object
const dataFinal = {
children: Object.keys(regions).map(key => (
{
id: key,
children: regions[key]
}
))
};
console.log(dataFinal);