The code below reduces an array into "named" objects (not sure if that's the correct terminology!)
It works, but I'm sure the code could be improved. There is some repetition going on in the reduce.
It checks if a key exists (if (accumulator[name])
). If not then initialize the results
array, if it does then push onto the results
array.
let response = {
columns: [
'n'
],
data: [
{
graph: {
nodes: [
{
id: '169',
labels: [
'Container'
],
properties: {
reference: 'REF002',
name: 'Cupboard',
id: '003'
}
}
],
relationships: []
}
},
{
graph: {
nodes: [
{
id: '170',
labels: [
'Container'
],
properties: {
reference: 'REF003',
name: 'Cupboard A',
id: '03a'
}
}
],
relationships: []
}
},
{
graph: {
nodes: [
{
id: '964',
labels: [
'Equipment'
],
properties: {
reference: 'REF004',
name: 'Cupboard B',
id: '03b'
}
}
],
relationships: []
}
}
]
}
const result = response.data.reduce(
(accumulator, currentValue, currentIndex, array) => {
const name = currentValue.graph.nodes[0].labels[0];
if (accumulator[name]) {
accumulator[name].results.push({
title: currentValue.graph.nodes[0].properties.name,
description: currentValue.graph.nodes[0].properties.reference
});
} else {
accumulator[name] = {
name,
results: [
{
title: currentValue.graph.nodes[0].properties.name,
description: currentValue.graph.nodes[0].properties.reference
}
]
};
}
return accumulator;
},
{}
);
console.clear();
console.log(result);
Output Required
{
Container: {
name: 'Container',
results: [
{
title: 'Cupboard',
description: 'REF002'
},
{
title: 'Cupboard A',
description: 'REF003'
}
]
},
Foo: {
name: 'Foo',
results: [
{
title: 'Cupboard B',
description: 'REF004'
}
]
}
}
2 Answers 2
As it looks like you are using ES6 syntax, you could also throw in some destructuring assignments, but I guess the main point is that one would need to create the output object only once. One could also extract the accumulation into a descriptive name:
byFirstLabel = (acc, {graph: {nodes: [node]}}) => {
let label = node.labels[0]
let { name: title, reference: description } = node.properties
let entry = { title, description }
acc[label] ? acc[label].results.push(entry) :
acc[label] = { name: label, results: [ entry ] }
return acc
}
response.data.reduce(byFirstLabel, {})
-
\$\begingroup\$ Excellent, I didn't know you could rename while destructuring an object. Could you explain what's going on in {graph: {nodes: [node]}} ? I think you're destructing the object as it's coming into the loop, but not sure exactly what magic is happening there:) \$\endgroup\$L G– L G2017年10月20日 15:09:36 +00:00Commented Oct 20, 2017 at 15:09
-
\$\begingroup\$ developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… AND developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… \$\endgroup\$L G– L G2017年10月20日 15:34:22 +00:00Commented Oct 20, 2017 at 15:34
You could try this. Keep the push
logic as the only way to add results
and separate out the initialisation logic to the ||
condition before itself
let response = {
columns: [
'n'
],
data: [{
graph: {
nodes: [{
id: '169',
labels: [
'Container'
],
properties: {
reference: 'REF002',
name: 'Cupboard',
id: '003'
}
}],
relationships: []
}
},
{
graph: {
nodes: [{
id: '170',
labels: [
'Container'
],
properties: {
reference: 'REF003',
name: 'Cupboard A',
id: '03a'
}
}],
relationships: []
}
},
{
graph: {
nodes: [{
id: '964',
labels: [
'Equipment'
],
properties: {
reference: 'REF004',
name: 'Cupboard B',
id: '03b'
}
}],
relationships: []
}
}
]
}
const result = response.data.reduce(
(accumulator, currentValue, currentIndex, array) => {
// Create a node const to avoid repeating it
const node = currentValue.graph.nodes[0];
const name = node.labels[0];
// Move initialization logic here
accumulator[name] = accumulator[name] || {
name,
results: []
};
// There is no need for the previous check anymore
accumulator[name].results.push({
title: node.properties.name,
description: node.properties.reference
});
return accumulator;
}, {}
);
console.clear();
console.log(result);