flatten an array of objects recursively - javascript

I have the following array of objects.. How can I flatten a multi-dimensional array of objects into a single dimensional array of objects?
[{
"name":"Locations",
"children":[
{
"name":"U.S."
},
{
"name":"Canada"
},
{
"name":"London"
}
]
},{
"name":"Benefits",
"children":[
{
"name":"U.S. Benefits",
"children":[
{
"name":"U.S. Benefits at a Glance"
},
{
"name":"U.S. Holiday Calendar"
}
]
},
{
"name":"London Benefits",
"children":[
{
"name":"London Benefits at a Glance"
},
{
"name":"London Holiday Calendar"
}
]
},
{
"name":"India Benefits",
"children":[
{
"name":"India Benefits at a Glance"
},
{
"name":"India Holiday Calendar"
}
]
}
]
}]
I need all the children to be in the same level as their parents in a single dimensional array.Any help will be appreciated.

You can do this without lodash with reduce and spread syntax. You just need to use recursion for children.
const data = [{"name":"Locations","children":[{"name":"U.S."},{"name":"Canada"},{"name":"London"}]},{"name":"Benefits","children":[{"name":"U.S. Benefits","children":[{"name":"U.S. Benefits at a Glance"},{"name":"U.S. Holiday Calendar"}]},{"name":"London Benefits","children":[{"name":"London Benefits at a Glance"},{"name":"London Holiday Calendar"}]},{"name":"India Benefits","children":[{"name":"India Benefits at a Glance"},{"name":"India Holiday Calendar"}]}]}]
const flatten = data => {
return data.reduce((r, { children, ...rest}) => {
r.push(rest);
if (children) r.push(...flatten(children));
return r;
}, [])
}
console.log(flatten(data))

Modified answer of #nenad-vracar to more reusable style:
const mock = [{"name":"Locations","children":[{"name":"U.S."},{"name":"Canada"},{"name":"London"}]},{"name":"Benefits","children":[{"name":"U.S. Benefits","children":[{"name":"U.S. Benefits at a Glance"},{"name":"U.S. Holiday Calendar"}]},{"name":"London Benefits","children":[{"name":"London Benefits at a Glance"},{"name":"London Holiday Calendar"}]},{"name":"India Benefits","children":[{"name":"India Benefits at a Glance"},{"name":"India Holiday Calendar"}]}]}];
const flatDeepByKey = (data, key) => {
return data.reduce((prev, el) => {
prev.push(el);
if (el[key]) {
prev.push(...flatDeepByKey(el[key], key))
};
return prev;
}, [])
};
console.log(flatDeepByKey(mock, 'children'));

You can perform a recursive loop-through and append to a running list by using a helper-function.
Note: If you want to remove the "root" dummy-node, just slice(1) the array.
var flattened = flattenTree({
"name" : "root", // Need a root entry, because the data is an array
"children" : getData() // Grab the data at the bottom
}, {
nameKey : 'name', // The name key; configurable option
childrenKey : 'children' // The children key; configurable option
});
console.log(flattened); // Flattened data printed to the console
function flattenTree(tree, options) {
options = options || {};
var nameKey = options.nameKey || 'name';
var childrenKey = options.childrenKey || 'children'
var resultList = [];
flattenTreeRecurse(tree, resultList, nameKey, childrenKey);
return resultList;
}
/** #private -- Recursive inner-call */
function flattenTreeRecurse(tree, list, nameKey, childrenKey) {
var entry = {};
entry[nameKey] = tree[nameKey];
list.push(entry);
if (tree[childrenKey] && tree[childrenKey].length > 0) {
tree[childrenKey].forEach(child => flattenTreeRecurse(child, list, nameKey, childrenKey));
}
}
function getData() {
return [{
"name": "Locations",
"children": [{
"name": "U.S."
}, {
"name": "Canada"
}, {
"name": "London"
}]
}, {
"name": "Benefits",
"children": [{
"name": "U.S. Benefits",
"children": [{
"name": "U.S. Benefits at a Glance"
}, {
"name": "U.S. Holiday Calendar"
}]
},
{
"name": "London Benefits",
"children": [{
"name": "London Benefits at a Glance"
}, {
"name": "London Holiday Calendar"
}]
},
{
"name": "India Benefits",
"children": [{
"name": "India Benefits at a Glance"
}, {
"name": "India Holiday Calendar"
}]
}
]
}];
}
.as-console-wrapper { top: 0; max-height: 100% !important; }

Related

How to filtering out the multiple nested object in Javascript object

Javascript
I have a nested array of objects, I'm trying to filter the given array of objects using a property from the third level of its array property value. For example, from the below array I like to filter the entire array using the property ListId: 10
Example
let test = {
"test":true,
"group":[
{
"name":"header",
"value":[
{
"id":"0",
"list":[
{
"ListId":10,
"name":"string1",
"state":"BY",
"techId":0
},
{
"ListId":11,
"name":"string2",
"state":"BY"
},
{
"ListId":12,
"name":"string3",
"state":"BY"
}
]
}
]
},
{
"name":"header2",
"value":[
{
"id":"01",
"list":[
{
"ListId":100,
"name":"string1",
"state":"BY",
"techId":0
},
{
"ListId":111,
"name":"string2",
"state":"BY"
},
{
"ListId":121,
"name":"string3",
"state":"BY"
}
]
}
]
}
]
}
Filtervalue with ListId = 10
Expected output :
{
"test":true,
"group":[
{
"name":"header",
"value":[
{
"id":"0",
"list":[
{
"ListId":10,
"name":"string1",
"state":"BY",
"techId":0
}
]
}
]
}
]
}
How can I use the filter method using javascript to get this expected result?
You can two it in two times :
First, filter the list arrays,
Secondly filter the groups array using the some method
let test= {
"test": true,
"group": [
{
"name": "header",
"value": [
{
"id": "0",
"list": [
{
"ListId": 10,
"name": "string1",
"state": "BY",
"techId": 0
},
{
"ListId": 11,
"name": "string2",
"state": "BY"
},
{
"ListId": 12,
"name": "string3",
"state": "BY"
}
]
}
]
},
{
"name": "header2",
"value": [
{
"id": "01",
"list": [
{
"ListId": 100,
"name": "string1",
"state": "BY",
"techId": 0
},
{
"ListId": 111,
"name": "string2",
"state": "BY"
},
{
"ListId": 121,
"name": "string3",
"state": "BY"
}
]
}
]
}
]
}
test.group.forEach(group => {
group.value.forEach(value => {
value.list = value.list.filter(list => list.ListId === 10)
})
})
test.group = test.group.filter(group => group.value.some(value => value.list.length > 0))
console.log(test)
Note : You should use plural names for you arrays, it helps understanding the data. For example lists not list for the array.
let z ={"group1": [
{
"name": "header",
"value": [
{
"id": 0,
"list": [
{
"ListId": 10,
"Name": "string1"
},
{
"ListId": 11,
"Name": "string2"
}
]
}
]
}
]}
// This function was written from understading that 'group1' is not a fixed property, but part of a dynamic list due to the number '1'
const getItemByListId = (list, listId) => {
const listKeys = Object.keys(list);
const selectedListKey = listKeys.find(key => {
const groupItems = list[key];
const selectedItem = groupItems.find(({ value: nestedItems }) => {
const selectedNestedItem = nestedItems.find(({ list }) => {
const selectedList = list.find(({ ListId }) => ListId === listId)
return selectedList;
});
return selectedNestedItem;
});
return selectedItem;
});
if (!selectedListKey) {
return null;
}
return list[selectedListKey];
};
console.log(getItemByListId(z, 10));

Group nested arrays in an object in js (Tree Vue.js)

This is my array of objects:
I am using vue.js , I need a tree like this to keep the structure of tree view: https://v2.vuejs.org/v2/examples/tree-view.html
[
{
"name": "",
"children": []
},
{
"name": "",
"children": [
{
"name": "Leggi",
"children": []
}
]
},
{
"name": "",
"children": [
{
"name": "Leggi",
"children": [
{
"name": "2010",
"children": []
}
]
}
]
},
{
"name": "",
"children": [
{
"name": "Leggi",
"children": [
{
"name": "2011",
"children": []
}
]
}
]
},
{
"name": "",
"children": [
{
"name": "Titoli",
"children": []
}
]
}
]
I need a function to retrive an object grouped by name with his childrens
{
"name": "",
"children": [
{
"name": "Leggi",
"children": [
{
"name": "2010",
"children": []
},
{
"name": "2011",
"children": []
}
],
"name": "Titoli",
"children": []
}
]
}
I would like to know if there it is a simple way (instead of writing a recursive function), like using lodash or something near it.
Thanks
I think that i have implemented a more readable answer:
const rootTree = [];
const putInTree = (tree, node) => {
let nodeInTree = tree.find(x => x.name === node.name);
if (!nodeInTree) {
nodeInTree = {name: node.name, children: []};
tree.push(nodeInTree);
}
if (node.children[0]) putInTree(nodeInTree.children, node.children[0])
}
nodes.forEach(node => putInTree(rootTree, node));
nodes here is your start array, let me know if this is ok
treeArchive.forEach(element => {
element.children.forEach(father => {
if (result.children.length != 0) {
cicleChildrens(result, father);
function cicleChildrens(padrePrecedente, nuovoPadre){
var brother = padrePrecedente.children.find(x => x.name == nuovoPadre.name);
if (brother != undefined) cicleChildrens(brother, nuovoPadre.children[0]);
else padrePrecedente.children.push(nuovoPadre);
};
}
else result.children.push(father);
});
});
This is currently my working code.. I'm struggling tryng to understand your code #chriss
Try this one:
function getGroupedByName(given) {
let result = given.reduce((a, b) => {
if(!a[b.name]) a[b.name] = [];
a[b.name] = [...a[b.name], ...b.children];
return a;
}, {});
result = Object.keys(result).map(key => ({name: key, children: getByName(result[key])}));
return result;
}
const o = []; // your initial object
getGroupedByName(o, "Leggi")
It is returning it as an array of objects having name and children props, as i am assuming first level can also have multiple different names, not all being ""
It goes first trough all elements in array and groups them into object with structure { name: children } where children is array of all children for same group.
For each children array it preforms same operation, going trough array and flattening it into { name: children } object.
At this moment we have following structure:
{ "": {
Leggi: {...}
}}
When everything is grouped, Object.keys loops trough all keys and breaks it into array where key is name and value children property

JSON extracted data returned too many times

I have a JSON with the following shape for ~50 _source entries:
{
"hits": [
{
"_source": {
"name": "henry",
"jobs": ["judge", "lawyer"]
}
},
{
"_source": {
"name": "henry",
"jobs": ["dev", "waitress"]
}
}
// ...
]
}
I want to extract only the first item in jobs node, i.e. judge, dev, ... and tried the following:
for (i in json.hits)
{
const jobExtract = json.hits[i]._source;
for (i=0; i<jobExtract.jobs.length; i++)
{
const firstItem = jobExtract.jobs[0];
console.log(firstItem);
}
}
I can extract the first item but it appears multiple times:
judge
judge
judge
dev
dev
Where have I gone wrong?
The first solution that pops up in my mind is using Reduce
Considering:
const hits = [
{
"_source": {
"name": "henry",
"jobs" : [ "judge","lawyer"] },
},
{
"_source": {
"name": "henry",
"jobs" : [ "dev","waitress"] },
}, ]
const result = hits.reduce((acc, item) => acc = [item._source.jobs[0], ...acc], [])
console.log(result) // ["dev", "judge"]
Hope this helps

Check for more than one key value pair in JSON data

I have JSON data formatted with multiple products which each have multiple variants and has data showing if the variant is available as well as the size of the variant.
"products":[
{
"variants":[
{
"available":true,
"selectedOptions":[
{
"name":"Size",
"value":"M"
}
]
},
{
"available":true,
"selectedOptions":[
{
"name":"Size",
"value":"L"
}
]
}
]
},
{
"variants":[
{
"available":true,
"selectedOptions":[
{
"name":"Size",
"value":"S"
}
]
},
{
"available":false,
"selectedOptions":[
{
"name":"Size",
"value":"L"
}
]
}
]
}
]
I want to traverse the JSON data and tell if the size of the product variant is large ("value":"L") and if the product is available ("available":true). I'm able to check for one or the other but I'm not sure how to check both at the same time. This is what I have so far:
o = products;
function traverse(o) {
for (var i in o) {
if(o[i] == true){
console.log([i,o[i]]);
}
if(o[i] == 'L'){
console.log([i,o[i]]);
}
if (o[i] !== null && typeof(o[i])=="object") {
traverse(o[i]);
}
}
}
console.log(o);
traverse(o);
}
You can use this approach to loop over the possible variants per product and identify the target according to availability and size.
This approach uses the function find and forEach for traversing the Object.
The function find looks for a selectedOption with size L and the nested forEach to check the availability.
var obj = { "products": [{ "variants": [{ "available": true, "selectedOptions": [{ "name": "Size", "value": "M" }] }, { "available": true, "selectedOptions": [{ "name": "Size", "value": "L" }] } ] }, { "variants": [{ "available": true, "selectedOptions": [{ "name": "Size", "value": "S" }] }, { "available": false, "selectedOptions": [{ "name": "Size", "value": "L" }] } ] } ]};
obj.products.forEach((p, i) => {
p.variants.forEach((v) => {
if (v.available) {
var found = v.selectedOptions.find((s) => s.value === 'L');
if (found) {
console.log(`Found a product at index '${i}' with variant ['${v.available}' | '${found.value}']`);
}
}
});
});
.as-console-wrapper { max-height: 100% !important; top: 0; }

Use array of strings to create a multi level object, parsing arrays into JS objects

I have this array that needs to be parsed into a useful object. The names of each value are a collection of namespaces separated by / characters. The values between each '/' need to be turned into a JS Objects property:
"status": [
{
"message": "OK",
"name": "/Computer",
"values": []
},
{
"name": "/Computer/CPU Usage",
"values": []
},
{
"name": "/Computer/CPU Temp",
"values": []
},
{
"name": "/Computer/hardware/memory",
"values": []
}
]
I need it to become this:
"status": {
"computer": {
"CPU Usage": {
"values": []
},
"CPU Temp": {
"values": []
},
"hardware": {
"memory": {
"values": []
}
}
}
}
So far I have done this:
var statii = status, // from above..
_parsedStatii = {};
for (var i = 0; statii.length < 0; i ++) {
var _nameSpaces = statii[i].name.split('/');
// Start at 1 because index 0 is empty (before the first slash)
if (!_parsedStatii[_nameSpaces[1]]) {
_parsedStatii[_nameSpaces[1]] = {};
}
if (!_parsedStatii[_nameSpaces[1]][_nameSpaces[2]])
_parsedStatii[_nameSpaces[1]][_nameSpaces[2]] = {};
if (!_parsedStatii[_nameSpaces[1]][_nameSpaces[2]][_nameSpaces[3]])
_parsedStatii[_nameSpaces[1]][_nameSpaces[2]][_nameSpaces[3]] = {};
if (!_parsedStatii[_nameSpaces[1]][_nameSpaces[2]][_nameSpaces[3]][_nameSpaces[4]])
_parsedStatii[_nameSpaces[1]][_nameSpaces[2]][_nameSpaces[3]][_nameSpaces[4]] = {};
}
Obviously it is no where near right, I have tried a lot of recursive functions but am at a bit of a loss. This example gives the clearest representation of what I am trying to achieve. Any ideas? (Please excuse code typos, it was paraphrased)
You could split the name and build an object upon.
var data = { "status": [{ "message": "OK", "name": "/Computer", "values": [] }, { "name": "/Computer/CPU Usage", "values": [] }, { "name": "/Computer/CPU Temp", "values": [] }, { "name": "/Computer/hardware/memory", "values": [] }] },
object = {};
data.status.forEach(function (a) {
a.name.slice(1).split('/').reduce(function (o, k) {
return o[k] = o[k] || {};
}, object).values = a.values;
});
console.log(object);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories