Covert flat array of objects to array of nested objects - javascript

i'm having trouble turning a flat array to a multi nested array
[{
code: "1",
text: "FatturaElettronicaHeader",
},
{
code: "1.1",
text: "DatiTrasmissione",
},
{
code: "1.1.5",
text: "ContattiTrasmittente",
},
{
code: "1.1.5.1",
text: "Telefono",
},
{
code: "1.1.5.2",
text: "Email",
},
{
code: "1.2",
text: "CedentePrestatore",
}
]
i want it so that if an array elements code includes the code of another array element, it should be a child of that element. So 1.1 and 1.2 are both children of 1. then 1.1.5 is child of 1.1 only. there should only be one of each object in the new array

You can create nested tree structure using forEach loop to iterate through array and then for each object you can use split to get path array and reduce method to create nested structure based on the path array.
const data = [{"code":"1","text":"FatturaElettronicaHeader"},{"code":"1.1","text":"DatiTrasmissione"},{"code":"1.1.5","text":"ContattiTrasmittente"},{"code":"1.1.5.1","text":"Telefono"},{"code":"1.1.5.2","text":"Email"},{"code":"1.2","text":"CedentePrestatore"}];
const result = [], level = {result}
data.forEach(e => {
e.code.split('.').reduce((r, k) => {
if(!r[k]) {
r[k] = {result: []}
r.result.push({...e, children: r[k].result})
}
return r[k];
}, level)
})
console.log(result);
You can also use two reduce methods instead of forEach loop to get the same result.
const data = [{"code":"1","text":"FatturaElettronicaHeader"},{"code":"1.1","text":"DatiTrasmissione"},{"code":"1.1.5","text":"ContattiTrasmittente"},{"code":"1.1.5.1","text":"Telefono"},{"code":"1.1.5.2","text":"Email"},{"code":"1.2","text":"CedentePrestatore"}];
const result = data.reduce((level, {code, ...rest}) => {
return code.split('.').reduce((r, k) => {
if(!r[k]) {
const children = [];
r[k] = {result: children}
r.result.push({code, ...rest, children})
}
return r[k]
}, level), level
}, {result: []}).result
console.log(result);

Related

How to index an array with map method consisting of objects and arrays

I have an array that is made from another array with the map method in JavaScript:
response = initialResponse.data.Resurs.map((item)=>({
KomRes:item.Kom,
levels:
[
...item.NumList.map((item)=>(
{
KomRes:item.Number,
})),
...item.SerList.map((item,index3)=>({
KomRes:"Serial: " + item.Ser,
})),
]}));
So, I have an array of 1 object and one array of objects. Now, I want to add indexes so that the parent object and all of its child objects have different indexes. One example would be:
[
{
KomRes:"abc"
id:1 // ==> Here the id is different to the levels objects id-s
levels:[{KomRes:"cde",id:2},{KomRes:"cdef",id:3}]
},
{
KomRes:"dfr"
id:4 // ==> Here the id is different to the levels objects id-s
levels:[{KomRes:"dsf",id:5},{KomRes:"sgsd",id:6}]
},
{
KomRes:"fgr"
id:7 // ==> Here the id is different to the levels objects id-s
levels:[{KomRes:"zizu",id:8},{KomRes:"hkl",id:9}]
},
]
As you can see, all of the objects have different ids (indexes). How can I achieve that?
I tried to add index to map method, but don't know how to achieve that with child map methods:
response = initialResponse.data.Resurs.map((item,index)=>({
KomRes:item.Kom,
id:index,
levels:
[
...item.NumList.map((item)=>(
{
KomRes:item.Number,
})),
...item.SerList.map((item,index3)=>({
KomRes:"Serial: " + item.Ser,
})),
]}));
Define a counter variable outside the function then on each iteration each object is given id property with an incremented value of the counter variable. Should there be any sub-arrays, they will be handled recursively by calling itself and passing in the sub-array.
const data=[{KomRes:"abc",id:null,levels:[{KomRes:"cde",id:null},{KomRes:"cdef",id:null}]},{KomRes:"ghi",id:null,levels:[{KomRes:"ijk",id:null},{KomRes:"ijkl",id:null}]},{KomRes:"mno",id:null,levels:[{KomRes:"omn",id:null},{KomRes:"omnp",id:null}]}];
let idx = 1;
function flatIndex(array) {
return array.map(obj => {
if (!obj.id) {
obj.id = idx++;
}
Object.values(obj).map(v => {
if (Array.isArray(v)) {
return flatIndex(v);
}
return obj;
});
return obj;
});
}
console.log(flatIndex(data));
Not sure if I understand well what you want to achieve, but you can declare a variable out of the scope and increment it along.
This gives the result you expect
const response = [
{ Kom: 'abc', NumList: [{ Number: "cde"}], SerList: [{ Ser: "cdef" }] },
{ Kom: 'dfr', NumList: [{ Number: "dsf"}], SerList: [{ Ser: "sgsd"}] },
{ Kom: 'fgr', NumList: [{ Number: "zizu"}], SerList: [{ Ser: "hkl"}] }
];
let lastId = 1; // index var to increment
const result = response.map((item) => ({
KomRes: item.Kom,
id: lastId++,
levels: [
...item.NumList.map((item) => ({
id: lastId++,
KomRes: item.Number,
})
),
...item.SerList.map((item) => ({
id: lastId++,
KomRes: "Serial: " + item.Ser,
})
),
]
})
);
console.log(result)

Javascript - filtering a list: how can I find an intersection between an array with objects including array and an array?

How can I filter a list (array with objects) with a filter list (array) and find intersections? I add to the filter array every time a user checks the checkbox clicking on particular filter. When user unchecks the checkbox I remove from filter array. Somehow whateever i try doing, i always return the entire reviews array including ALL not filtered items. Why? Thanks!!
const reviews = [
{
title: "item 1",
filter_results: {
features: ["message", "call"],
pricing: ["Business", "Free", "Whatever"],
rating: [1]
}
},
{
title: "item 2",
filter_results: {
features: ["call", "copy", "paste"],
pricing: ["Business"],
rating: [1]
}
},
{
title: "item 3",
filter_results: {
features: ["copy", "connect", "wifi"],
pricing: ["Free",
rating: [2]
}
}
]
const filteredReviews = {
pricing_options: ["Business"],
popular_features: ["copy, call"],
rating: [1, 2]
}
const update = (reviews, categoryName) => {
if (categoryName) {
return reviews.filter(review => {
return review.filter_results[categoryName].filter(value => {
if (filteredReviews[categoryName].includes(value)) {
return review
}
})
})
} else {
return reviews
}
}
update(reviews, "pricing")
Return a boolean on filter callback, and do a better filtering mechanism:
const update = (reviews, filters) => {
if (filters) {
return reviews.filter(review =>
Object.entries(filters)
// Change to `some` if the filters are OR'ed instead of AND'ed
.every(
([filter_key, filter_values]) =>
// Change `some` to `every` if all elements in the
// userFilter[*] array MUST be matched instead of some of them
filter_values.some( (filter_value) =>
review.filter_results[filter_key]
.includes(filter_value)
)
)
)
} else {
return reviews
}
}
// Fix variables names:
// - `userFilters` contains the filters selected by the user
// - `filteredReviews` contains the array of reviews, resulting from
// filtering the reviews using the `userFilters`
// Fix key names: Use same keys than in reviews, instead of:
// - `pricing_options` => `pricing`
// - `popular_features` => `features`
const userFilters = {
pricing: ["Business"],
// Transformed/fixed to 2 values. Was it a typo?
features: ["copy", "call"],
};
const filteredReviews = update(reviews, userFilters);
Filter callback function should return a "boolean", you are returning arrays which evaluate always to "true".

How to recursively add object key-value pairs to an array of objects with child arrays?

I'm still learning the ropes of recursion, and I would like to add 'isChecked: false' to every object in this nested tree of arrays.
const nestedMap = (arr) => {
const result = arr.map(row => {
if (row.children.length) {
const children = row.children.map(child => {
return { ...child, isChecked: false };
});
return { ...row, isChecked: false, children };
} else {
return { ...row, isChecked: false };
}
});
return result[0].children[0]
}
nestedMap([{title: 'page1', children: [{title: 'page2', children: [{title: 'page4'}]}, {title: 'page3'}]}, {title: 'page5', children: []}])
Here's my result - as you can see I'm successfully updating the first set of children, but not the rest.
{ title: 'page2',
children: [ { title: 'page4' } ],
isChecked: false }
I know I'm supposed to call the function somewhere inside itself, but I'm an idiot. As always, any help would be greatly appreciated. Thank you.
Your if-statement if (row.children.length) { checks if row.children is an array with length > 0, so here's where you'd want to call nestedMap again.
Try something like this:
const nestedMap = (arr) => {
const result = arr.map(row => {
// check if row.children exists AND if its length exists / is greater than 0
if (row.children && row.children.length) {
const children = nestedMap(row.children);
return { ...row, isChecked: false, children };
} else {
return { ...row, isChecked: false };
}
});
// Note: You should probably return the entire result here, not result[0].children[0]
return result;
}
let output = nestedMap([{title: 'page1', children: [{title: 'page2', children: [{title: 'page4'}]}, {title: 'page3'}]}, {title: 'page5', children: []}])
console.log(output)
Notice I made 3 changes:
if (row.children && row.children.length) {
I added the check to make sure row.children exists, before checking row.children.length
const children = nestedMap(row.children);
This is where we do the recursion :)
return result;
This will return the entire result (the original nested array of objects, with isChecked: false in every object).
Note: I'm not sure why you ended your function with result[0].children[0], which will only return the original array's first item's first child?
Another possibility is to write a more generic nestedMap implementation, and then pass it a function which adds your property. Here's how I might write it:
const nestedMap = (fn) => (xs) =>
xs .map (({children, ...rest}) => fn ({
... rest,
... (children ? {children: nestedMap (fn) (children)} : {})
}))
const addProp = (key, val) => (obj) =>
({... obj, [key]: val})
const pages = [{title: 'page1', children: [{title: 'page2', children: [{title: 'page4'}]}, {title: 'page3'}]}, {title: 'page5', children: []}]
console .log (
nestedMap (addProp ('checked', false)) (pages)
)
This version does not mutate our original, but creates a new array full of new objects. (It does not do a full clone; other properties of our nodes may be shared by reference.)
addProp is simple enough: it takes a key and a value and returns a function which takes an object, returning a new object with all its properties and the new key/value.
nestedMap recursively maps a function over a node: {..., children: [more nodes]} structure. The transformation function supplied to it is called first (recursively) on the children of each node and then on the node itself.

How to clone new array of objects by selecting particular properties from another?

So,
I am receiving the data that has the following information:
{
"data":[
{
"vote_count":22222,
"id":299537,
"ready":false,
},
{
"vote_count":2850,
"id":299534,
"ready":true,
},
]
}
Now I need to make a new object that would contain the same structure but with some properties, ie:
{
"data": [
{
"ready":false,
},
{
"ready":true,
}
]
}
I need the solution that is scalable, imagine having a set of data with 50 properties for example. Also, I did find solutions with objects, but never with array of objects.
Thanks guys, I've been busting my head for three hours now.
You could use destrcuturing and shorthand property names to create new objects like this:
const input={"data":[{"vote_count":22222,"id":299537,"ready":false,},{"vote_count":2850,"id":299534,"ready":true,},]}
const data = input.data.map(({ ready }) => ({ ready }))
console.log({ data })
If you want to get a bunch of properties, you could create an array of properties you need. Then use Object.assign() or reduce to create a subset of each object like this:
const input={"data":[{"vote_count":22222,"id":299537,"ready":false,},{"vote_count":2850,"id":299534,"ready":true,},]}
const properties = ["vote_count", "ready"]
const data = input.data.map(a =>
Object.assign({}, ...properties.map(p => ({ [p]: a[p] })))
)
/* You could also use reduce like this:
input.data.map(a => properties.reduce((r, p) => ({ ...r, [p]: a[p] }), {}))
*/
console.log({ data })
Map the properties you want
var obj1 = {
"data":[
{
"vote_count":22222,
"id":299537,
"ready":false,
},
{
"vote_count":2850,
"id":299534,
"ready":true,
},
]
}
var obj2 = {}
obj2.date = obj1.data.map(data => ({ ready: data.ready}));
console.log(obj2)
You can do it using Array#map method and Array#reduce method
const input = {
"data": [{
"vote_count": 22222,
"id": 299537,
"ready": false,
},
{
"vote_count": 2850,
"id": 299534,
"ready": true,
},
]
}
const extract = ['ready']
const data = input.data.map(o => extract.reduce((obj, k) => (obj[k] = o[k], obj), {}))
console.log({ data })

Creating a new key from child elements in Json with Javascript

I want to create new key from child values inside the json with ES6/ES5. I tried with arrow functions but i couldn't get the result. Firstly you can see my part of json in below,
[
{
matchId:307,
matchStatusId:5,
matchHomeScore:0,
matchAwayScore:0,
matchTime:0,
homeClubId:608,
homeClub:{
clubId:608,
clubName:"Annaba"
},
awayClubId:609,
awayClub:{
clubId:609,
clubName:"Bazer Sakhra"
},
leagues:[
{
leagueId:65,
parentLeagueId:null,
leagueName:"ALGERIA"
},
{
leagueId:66,
parentLeagueId:65,
leagueName:"Algeria Cup"
}
]
},
]
I want to create new parent key. It will get the values from values of child items and it will combine. Child item numbers changeable. Not everytime 2 items.
leaguesGeneral:"ALGERIA - Algeria Cup"
leaguesGeneral:"ALGERIA - Algeria Cup"
leagues: [
{
leagueId:65,
parentLeagueId:null,
leagueName:"ALGERIA"
},
{
leagueId:66,
parentLeagueId:65,
leagueName:"Algeria Cup"
}
]
I found this method. But it combines everything from the parent.
data = data.map(function (x) {
var keys = Object.keys(x);
x.newKeyValue = keys.map(key => x[key]).join('-');
return x;
});
The leaguesGeneral key can be added to each object in the array using
o.leagues.map(({ leagueName }) => leagueName).join(' - ')
const data = [
{
matchId:307,
matchStatusId:5,
matchHomeScore:0,
matchAwayScore:0,
matchTime:0,
homeClubId:608,
homeClub:{
clubId:608,
clubName:"Annaba"
},
awayClubId:609,
awayClub:{
clubId:609,
clubName:"Bazer Sakhra"
},
leagues:[
{
leagueId:65,
parentLeagueId:null,
leagueName:"ALGERIA"
},
{
leagueId:66,
parentLeagueId:65,
leagueName:"Algeria Cup"
}
]
},
];
const result = data.map(o => {
if (o.leagues) {
o.leaguesGeneral = o.leagues.map(({ leagueName }) => leagueName).join(' - ');
}
return o;
})
console.log(result)

Categories