Javascript/ES6 generate nested array from 1 level object - javascript

i do have an object coming from an API like this:
this.Variations = [
{
attributes: [
{name: "Cor", option: "Preto"},
{name: "Tamanho", option: "P"},
{name: "Tecido", option: "Algodao"},
],
id : 1
},
{
attributes: [
{name: "Cor", option: "Preto"},
{name: "Tamanho", option: "P"},
{name: "Tecido", option: "Elastano"},
],
id : 2
},
{
attributes: [
{name: "Cor", option: "Preto"},
{name: "Tamanho", option: "M"},
{name: "Tecido", option: "Algodao"},
],
id : 3
}, ...
and i have to generate a nested array from it, in order to create the experience i need, which is the user selecting attributes one by time, and loading the possible options.
I am struggling with this for a long time now and got nowhere.
I believe I need an array similar to that:
array['Preto']['P']['Algodao'] = info;
array['Preto']['P']['Elastano'] = info;
array['Preto']['M']['Algodao'] = info;
Any tips on how to do that?

Assuming that by info you meant id value, try this approach
var output = variations.map( s => ({ [s.attributes[0].option ] :
{ [s.attributes[1].option ] :
{ [ s.attributes[2].option] : s.id } } }) );
Demo
var variations = [{
attributes: [{
name: "Cor",
option: "Preto"
},
{
name: "Tamanho",
option: "P"
},
{
name: "Tecido",
option: "Algodao"
},
],
id: 1
},
{
attributes: [{
name: "Cor",
option: "Preto"
},
{
name: "Tamanho",
option: "P"
},
{
name: "Tecido",
option: "Elastano"
},
],
id: 2
},
{
attributes: [{
name: "Cor",
option: "Preto"
},
{
name: "Tamanho",
option: "M"
},
{
name: "Tecido",
option: "Algodao"
},
],
id: 3
}
];
var output = variations.map( s => ({ [s.attributes[0].option ] : { [s.attributes[1].option ] : { [ s.attributes[2].option] : s.id } } }) );
console.log( output );

This code works regardless of the number of items in attributes arrays:
var Variations = [
{
attributes: [
{name: "Cor", option: "Preto"},
{name: "Tamanho", option: "P"},
{name: "Tecido", option: "Algodao"},
{name: "Foo", option: "Bar"},
],
id : 1
},
{
attributes: [
{name: "Cor", option: "Preto"},
{name: "Tamanho", option: "P"},
{name: "Tecido", option: "Elastano"},
],
id : 2
},
{
attributes: [
{name: "Cor", option: "Preto"},
{name: "Tamanho", option: "M"},
{name: "Tecido", option: "Algodao"},
],
id : 3
}];
const getLevelValue = (rec, att, index) => index === rec.attributes.length - 1 ? rec.id : {},
getData = variations => variations.map(rec => rec.attributes.
reduce(({acc, current}, att, index) =>
(current = current ? current[att.option] = getLevelValue(rec, att, index) :
acc[att.option] = getLevelValue(rec, att, index), {acc, current}),
{acc: {}}).acc);
console.log(getData(Variations));

You could iterate the array and the attributes for a nested object and assign a value at the leaves.
var data = [{ attributes: [{ name: "Cor", option: "Preto" }, { name: "Tamanho", option: "P" }, { name: "Tecido", option: "Algodao" }], id: 1 }, { attributes: [{ name: "Cor", option: "Preto" }, { name: "Tamanho", option: "P" }, { name: "Tecido", option: "Elastano" }], id: 2 }, { attributes: [{ name: "Cor", option: "Preto" }, { name: "Tamanho", option: "M" }, { name: "Tecido", option: "Algodao" }], id: 3 }],
result = {};
data.forEach(function (a) {
a.attributes.reduce(function (r, b, i, bb) {
return r[b.option] = i + 1 === bb.length
? a.id
: r[b.option] || {};
}, result);
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

Underscore.js: filter objects with nested array of objs

I am trying to filter top-level objects based on nested data (filtering is also needed for nested objects).
Filter params: All records with categoryValue A.
const table = [
{
name: 'Bob',
detail: [
{ category: 'Employeer', categoryValue: 'A' },
{ category: 'Employeer', categoryValue: 'B' },
],
},
{
name: 'Alice',
detail: [
{ category: 'Employeer', categoryValue: 'C' },
{ category: 'Employeer', categoryValue: 'D' },
],
},
];
I am expecting the following result
const fileredTable = [
{
//this
name: 'Bob',
detail: [
//and only this
{ category: 'Employeer', categoryValue: 'A' }
],
}
];
Ok, after several tries i found this solution:
const filteredTable = table.flatMap((item) => {
const filteredDetail = item.detail.filter((e) => e.categoryValue === 'A')
return filteredDetail.length !== 0 ? {...item, detail: filteredDetail} : []
})

How can I filter/search multiple subarrays?

I am working on a search function for a parent child structure with locations (city,suburb,street).
I want to search for a street name and get the results but keeping the structure.
The array structure is like this:
const cities = [
{
name: 'city1', sublocations: [
{
name: 'suburb1', sublocations: [
{name: 'street1'},
{name: 'street2'},
{name: 'street3'},
]
}, {
name: 'suburb2', sublocations: [
{name: 'street1'},
{name: 'street2'},
{name: 'street3'},
]
}
]
}
];
When I search for street1 for example I want to get this:
const cities = [
{
name: 'city1', sublocations: [
{
name: 'suburb1', sublocations: [
{name: 'street1'},
]
}, {
name: 'suburb2', sublocations: [
{name: 'street1'},
]
}
]
}
];
I tried it with the array.filter function but it overwrites the data and when I remove the search string the rest of the array doesn't come back.
I think this snippet should do the trick :
var cities = [
{name :'city1', sublocations:[
{name :'suburb1', sublocations:[
{name :'street1'},
{name :'street2'},
{name :'street3'},
]},
{name :'suburb2', sublocations:[
{name :'street1'},
{name :'street2'},
{name :'street3'},
]}
]}
];
let findStreet = function(streetName) {
return cities.map(city=>
({...city, sublocations:city.sublocations.map(suburb=>
({...suburb, sublocations:suburb.sublocations.filter(
street=>street.name===streetName
)
})
)})
)
}
console.log(findStreet("street1"));
If you need a bit more flexibility you could generalize the location type by adding a type key, e.g.:
{ type: 'city', name: 'city1', locations: [] }
And then use a recursive filter function to make a decision for each node in the tree, e.g.:
const filterTree = (nodes, childrenKey, selector) => {
return nodes.filter(selector).map(node => {
return node.hasOwnProperty(childrenKey)
? {...node, [childrenKey]: filterTree(node[childrenKey], childrenKey, selector)}
: node;
});
};
Example:
const locations = [
{
type: 'city', name: 'city1', locations: [
{
type: 'suburb', name: 'suburb1', locations: [
{
type: 'street',
name: 'street1'
},
{
type: 'street',
name: 'street2'
},
{
type: 'street',
name: 'street3'
},
]
}, {
type: 'suburb', name: 'suburb2', locations: [
{
type: 'street',
name: 'street1'
},
{
type: 'street',
name: 'street2'
},
{
type: 'street',
name: 'street3'
},
]
}
]
}
];
const filterTree = (nodes, childrenKey, selector) => {
return nodes.filter(selector).map(node => {
return node.hasOwnProperty(childrenKey)
? {...node, [childrenKey]: filterTree(node[childrenKey], childrenKey, selector)}
: node;
});
};
// example
const filtered = filterTree(locations, 'locations', ({type, name}) => {
return type === 'city' // allow all cities
|| (type === 'suburb' && name === 'suburb2') // but only specific suburbs
|| (type === 'street' && name === 'street2') // and streets;
});
console.log(filtered);

Restructuring Javascript Arrays

EDIT: Thanks for the replies!
I am kind of new to working with Javascript (sorry for probably the bad code). I've mainly only worked with HTML and CSS. I have been trying to experiment with Javascript, but I've been stuck on this forever. The outcome is not what I want it to be.
My code:
I want to make the array easier to use, by putting the values under the same category, but my code's output is not exactly what I want it to be like.
var data = {
idName: "idName",
valueRanges: [{
range: "range",
majorDimension: "ROWS",
values: [
[
"ID",
"Category",
"Name"
],
[
"1",
"Category1",
"Name1"
],
[
"2",
"Category1",
"Name2"
],
[
"3",
"Category2",
"Name3"
],
[
"4",
"Category1",
"Name4"
]
]
}]
}
var rows = [];
let batchRowValues = data.valueRanges[0].values
for (let i = 1; i < batchRowValues.length; i++) {
let rowObject = {};
for (let j = 0; j < batchRowValues[i].length; j++) {
rowObject[batchRowValues[0][j]] = batchRowValues[i][j];
}
rows.push(rowObject);
}
var newArray = rows.reduce(function(acc, curr) {
var findIfNameExist = acc.findIndex(function(item) {
return item.Category === curr.Category;
})
if (findIfNameExist === -1) {
let obj = {
'Category': curr.Category,
'value': [curr]
}
acc.push(obj)
} else {
acc[findIfNameExist].value.push(curr)
}
return acc;
}, []);
console.log('------------')
console.log('input: ' + JSON.stringify(data, null, 2))
console.log('------------')
console.log('output: ' + JSON.stringify(newArray, null, 2))
My code's output:
[
{
Category: "Category1",
value: [
{
Category: "Category1",
ID: "1",
Name: "Name1"
},
{
Category: "Category1",
ID: "2",
Name: "Name2"
},
{
Category: "Category1",
ID: "4",
Name: "Name4"
}
]
},
{
Category: "Category2",
value: [
{
Category: "Category2",
ID: "3",
Name: "Name3"
}
]
}
]
How I want it to look:
[
{
Category: "Category1",
values: [
{
ID: "1",
Name: "Name1"
},
{
ID: "2",
Name: "Name2"
},
{
ID: "4",
Name: "Name4"
}
]
},
{
Category: "Category2",
values: [
{
ID: "3",
Name: "Name3"
},
]
},
]
I want to learn! I appreciate any help.
You can use reduce.
Here idea is
Create a object and use category as key
If a category is already found than push the desired object in it's value property. if not than we create a new one with suitable data.
I am using object here instead of array directly is because i can directly access element using key where as in array i need to loop through each time and check existence of value
var data = {idName: "idName",valueRanges: [{range: "range",majorDimension: "ROWS",values: [["ID","Category","Name"],["1","Category1","Name1"],["2","Category1","Name2"],["3","Category2","Name3"],["4","Category1","Name4"]]}]}
var rows = [];
let batchRowValues = data.valueRanges[0].values.slice(1,)
let op = batchRowValues.reduce((op,[ID,Category,Name]) => {
if( op[Category] ){
op[Category].value.push({ID,Name})
} else {
op[Category] = {
Category,
value: [{ID,Name}]
}
}
return op
},{})
console.log(Object.values(op))
Try (t={}, result in r)
data.valueRanges[0].values.slice(1).map( ([i,c,n])=>
(t[c]=t[c]||{Category:c,values:[]}, t[c].values.push({ID:i, Name:n})) );
let r= Object.values(t);
var data =
{
idName: "idName",
valueRanges: [
{
range: "range",
majorDimension: "ROWS",
values: [
[
"ID",
"Category",
"Name"
],
[
"1",
"Category1",
"Name1"
],
[
"2",
"Category1",
"Name2"
],
[
"3",
"Category2",
"Name3"
],
[
"4",
"Category1",
"Name4"
]
]
}
]
}
let t={};
data.valueRanges[0].values.slice(1).map( ([i,c,n])=>
(t[c]=t[c]||{Category:c,values:[]}, t[c].values.push({ID:i, Name:n})) );
let r= Object.values(t);
console.log(r);

join object with the same name into one item

I want to join the objects with the same name into one item array and i don't know how to do it.
I have an array like this :
[
{ name: 'core/core.js',
item: [ [Object] ] },
{ name: 'users/admin.js',
item: [ [Object] ] },
{ name: 'users/admin.js',
item: [ [Object] ] }
]
and i want to have something similar to this :
[
{ name: 'core/core.js',
item: [ [Object] ] },
{ name: 'users/admin.js',
item: [
[ [Object] ],
[ [Object] ]
]
},
]
Here is my code:
const pathFile = [
{ name: 'core/core.js', item: [{ name: 'core' }] },
{ name: 'users/admin.js', item: [{ name: 'admin1' }] },
{ name: 'users/admin.js', item: [{ name: 'admin2' }] },
];
const obj = pathFile.reduce((a, name) => {
if (!a[name]) {
a[name] = { name, files: [] };
}
a[name].files.push({ name });
return a;
}, {});
const arr = Object.values(obj);
console.log(JSON.stringify(arr));
Your problem is that you are using the wrong value for keys inside your reduce() loop. In your code name refers to the whole object inside the reduce so you would need to use name.name for the key because each item, which you are calling name inside the reduce loop has a name property. It might be better to use a different parameter name such as curr so it's clearer.
const pathFile = [{ name: 'core/core.js', item: [{ name: 'core' }] },{ name: 'users/admin.js', item: [{ name: 'admin1' }] },{ name: 'users/admin.js', item: [{ name: 'admin2' }] },];
const obj = pathFile.reduce((a, curr) => {
if (!a[curr.name]) a[curr.name] = curr
else a[curr.name].item.push(...curr.item);
return a;
}, {});
const arr = Object.values(obj);
console.log(arr)

Setting array keys dynamically based on length

Given an array in this format:
[
[{
name: "name",
value: "My-name"
},
{
name: "qty",
value: "1"
},
{
name: "url",
value: "test.com"
},
{
name: "comment",
value: "my-comment"
}
],
[{
name: "name",
value: "My-name2"
},
{
name: "qty",
value: "3"
},
{
name: "url",
value: "test2.com"
}
],
[{
name: "name",
value: "My-name3"
},
{
name: "qty",
value: "1"
},
{
name: "url",
value: "test3.com"
},
{
name: "comment",
value: "my-comment3"
}
]
]
I'm looking to switch that to:
[
[
{ name: "My-name" },
{ qty: "1" },
{ url: "test.com" },
{ comment: "my-comment", }
],[
{ name: "My-name2" },
{ qty: "3" },
{ url: "test2.com",
],[
{ name: "My-name3", },
{ qty: "1", },
{ url: "test3.com", },
{ comment: "my-comment3", }
]
]
In other words, swapping out the array keys but maintaining the object structure within each array element.
I've tried looping over each element and can swap the keys out using something like:
newArray[iCount][item.name] = item.value;
However I'm then struggling to preserve the object order. Note that the comment field may or may not appear in the object.
With Array.map() function:
var arr = [
[{name:"name",value:"My-name"},{name:"qty",value:"1"},{name:"url",value:"test.com"},{name:"comment",value:"my-comment"}],
[{name:"name",value:"My-name2"},{name:"qty",value:"3"},{name:"url",value:"test2.com"}],
[{name:"name",value:"My-name3"},{name:"qty",value:"1"},{name:"url",value:"test3.com"},{name:"comment",value:"my-comment3"}]
],
result = arr.map(function(a){
return a.map(function(obj){
var o = {};
o[obj.name] = obj.value
return o;
});
});
console.log(result);
Check my moreBetterOutput value. I think will be better.
If you still need a result like your example in the question then you can check output value.
const input = [
[
{
name:"name",
value:"My-name"
},
{
name:"qty",
value:"1"
},
{
name:"url",
value:"test.com"
},
{
name:"comment",
value:"my-comment"
}
],
[
{
name:"name",
value:"My-name2"
},
{
name:"qty",
value:"3"
},
{
name:"url",
value:"test2.com"
}
],
[
{
name:"name",
value:"My-name3"
},
{
name:"qty",
value:"1"
},
{
name:"url",
value:"test3.com"
},
{
name:"comment",
value:"my-comment3"
}
]
]
const output = input.map(arr => arr.map(obj => ({[obj.name]: obj.value})))
const moreBetterOutput = output.map(arr => arr.reduce((acc, item, index) => {
acc[Object.keys(item)[0]] = item[Object.keys(item)[0]];
return acc;
}, {}) )
//console.log(output);
console.log(moreBetterOutput);
Another map function:
const result = array.map( subarray =>
Object.assign(...subarray.map( ({name, value}) => ({ [name] : value }) ))
);

Categories

Resources