How do I "flatten" an object into an array of arrays - javascript

I have this object:
{
"value": "face",
"next": [
{
"value": "tace",
"next": [
{
"value": "tale",
"next": [
{
"value": "talk",
"next": []
}
]
},
{
"value": "tack",
"next": [
{
"value": "talk",
"next": []
}
]
}
]
},
{
"value": "fack",
"next": [
{
"value": "tack",
"next": [
{
"value": "talk",
"next": []
}
]
},
{
"value": "falk",
"next": [
{
"value": "talk",
"next": []
}
]
}
]
}
]
}
What is the best way to iterate over it and create this array of arrays:
[
["face", "tace", "tale", "talk"],
["face", "tace", "tack", "talk"],
["face", "fack", "tack", "talk"],
["face" ,"fack", "falk", "talk"]
]
I basically want to "flatten" the object into the array format by traversing down each branch of the object and producing an array of strings for each branch.

You can do this by creating recursive function using reduce method, that will store previous values and when there is no elements in next property it will current copy push to result array.
const data = {"value":"face","next":[{"value":"tace","next":[{"value":"tale","next":[{"value":"talk","next":[]}]},{"value":"tack","next":[{"value":"talk","next":[]}]}]},{"value":"fack","next":[{"value":"tack","next":[{"value":"talk","next":[]}]},{"value":"falk","next":[{"value":"talk","next":[]}]}]}]}
const flatten = (obj, prev = []) => {
const next = prev.concat(obj.value)
return obj.next.reduce((r, e) => {
if(e.next.length) r.push(...flatten(e, next))
else r.push(next.slice().concat(e.value));
return r;
}, [])
}
const result = flatten(data);
console.log(result);

You can use recursion with Array.forEach() to iterate the next property, and add the previous items. When next is empty, take everything, flatten, and push to the result:
const flatAll = (data) => {
const result = [];
const fn = ({ value, next }, prev = []) => {
if(next.length) next.forEach(o => fn(o, [prev, value]));
else result.push([prev, value].flat(Infinity));
};
fn(data);
return result;
}
const data = {"value":"face","next":[{"value":"tace","next":[{"value":"tale","next":[{"value":"talk","next":[]}]},{"value":"tack","next":[{"value":"talk","next":[]}]}]},{"value":"fack","next":[{"value":"tack","next":[{"value":"talk","next":[]}]},{"value":"falk","next":[{"value":"talk","next":[]}]}]}]};
const result = flatAll(data);
console.log(result);

You could use an independent recursive function and collect the last item and build an arrayof the given values for every level.
const
flat = (value, next) => next
.reduce((r, { value, next }) => {
if (next.length) r.push(...flat(value, next));
else r.push([value]);
return r;
}, [])
.map(q => [value, ...q]);
var data = { value: "face", next: [{ value: "tace", next: [{ value: "tale", next: [{ value: "talk", next: [] }] }, { value: "tack", next: [{ value: "talk", next: [] }] }] }, { value: "fack", next: [{ value: "tack", next: [{ value: "talk", next: [] }] }, { value: "falk", next: [{ value: "talk", next: [] }] }] }] },
result = flat(data.value, data.next);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

array of object destruction javascript [duplicate]

I have this array of objects, within it I have another array of objects:
[
{
id: 1,
country: [
{
id: "5a60626f1d41c80c8d3f8a85"
},
{
id: "5a6062661d41c80c8b2f0413"
}
]
},
{
id: 2,
country: [
{
id: "5a60626f1d41c80c8d3f8a83"
},
{
id: "5a60626f1d41c80c8d3f8a84"
}
]
}
];
How to get flat array of country like this:
[
{ id: "5a60626f1d41c80c8d3f8a85" },
{ id: "5a6062661d41c80c8b2f0413" },
{ id: "5a60626f1d41c80c8d3f8a83" },
{ id: "5a60626f1d41c80c8d3f8a84" }
];
without using a forEach and a temp variable?
When I did:
(data || []).map(o=>{
return o.country.map(o2=>({id: o2.id}))
})
I got the same structure back.
Latest edit
All modern JS environments now support Array.prototype.flat and Array.prototype.flatMap
const data=[{id:1,country:[{id:"5a60626f1d41c80c8d3f8a85"},{id:"5a6062661d41c80c8b2f0413"}]},{id:2,country:[{id:"5a60626f1d41c80c8d3f8a83"},{id:"5a60626f1d41c80c8d3f8a84"}]}];
console.log(
data.flatMap(
(elem) => elem.country
)
)
Old answer
No need for any ES6 magic, you can just reduce the array by concatenating inner country arrays.
const data=[{id:1,country:[{id:"5a60626f1d41c80c8d3f8a85"},{id:"5a6062661d41c80c8b2f0413"}]},{id:2,country:[{id:"5a60626f1d41c80c8d3f8a83"},{id:"5a60626f1d41c80c8d3f8a84"}]}];
console.log(
data.reduce(
(arr, elem) => arr.concat(elem.country), []
)
)
If you want an ES6 feature (other than an arrow function), use array spread instead of the concat method:
const data=[{id:1,country:[{id:"5a60626f1d41c80c8d3f8a85"},{id:"5a6062661d41c80c8b2f0413"}]},{id:2,country:[{id:"5a60626f1d41c80c8d3f8a83"},{id:"5a60626f1d41c80c8d3f8a84"}]}];
console.log(
data.reduce(
(arr, elem) => [...arr, ...elem.country], []
)
)
Note: These suggestions would create a new array on each iteration.
For efficiency, you have to sacrifice some elegance:
const data=[{id:1,country:[{id:"5a60626f1d41c80c8d3f8a85"},{id:"5a6062661d41c80c8b2f0413"}]},{id:2,country:[{id:"5a60626f1d41c80c8d3f8a83"},{id:"5a60626f1d41c80c8d3f8a84"}]}];
console.log(
data.reduce(
(arr, elem) => {
for (const c of elem.country) {
arr.push(c);
}
return arr;
}, []
)
)
const raw = [
{
id: 1,
country: [
{
id: "5a60626f1d41c80c8d3f8a85"
},
{
id: "5a6062661d41c80c8b2f0413"
}
]
},
{
id: 2,
country: [
{
id: "5a60626f1d41c80c8d3f8a83"
},
{
id: "5a60626f1d41c80c8d3f8a84"
}
]
}
];
const countryIds = raw
.map(x => x.country)
.reduce((acc, curr) => {
return [
...acc,
...curr.map(x => x.id)
];
}, []);
console.log(countryIds)
This, works, just concat the nested arrays returned by your solution
let arr = [{ "id": 1,
"country": [{
"id": "5a60626f1d41c80c8d3f8a85",
},
{
"id": "5a6062661d41c80c8b2f0413",
}
]
},
{
"id": 2,
"country": [{
"id": "5a60626f1d41c80c8d3f8a83",
},
{
"id": "5a60626f1d41c80c8d3f8a84",
}
]
}
];
//If you want an array of country objects
console.log([].concat.apply(...(arr || []).map(o=> o.country)))
//If you can an array od country ids
console.log([].concat.apply(...(arr || []).map(o=> o.country.map(country => country.id))))
Ayush Gupta's solution will work for this case. But I would like to provide other solution.
const arr = [
{
id: 1,
country: [
{
id: '5a60626f1d41c80c8d3f8a85'
},
{
id: '5a6062661d41c80c8b2f0413'
}
]
},
{
id: 2,
country: [
{
id: '5a60626f1d41c80c8d3f8a83'
},
{
id: '5a60626f1d41c80c8d3f8a84'
}
]
}
];
const ids = arr.reduce(
(acc, {country}) => [
...acc,
...country.map(({id}) => ({
id
}))
],
[]
);
console.log(ids);
For JSON string data, it can be done during parsing too :
var ids = [], json = '[{"id":1,"country":[{"id":"5a60626f1d41c80c8d3f8a85"},{"id":"5a6062661d41c80c8b2f0413"}]},{"id":2,"country":[{"id":"5a60626f1d41c80c8d3f8a83"},{"id":"5a60626f1d41c80c8d3f8a84"}]}]';
JSON.parse(json, (k, v) => v.big && ids.push(v));
console.log( ids );
I am not sure why noone mentioned flat() (probably for large arrays, it might be less performant)
(data || []).map(o=>{
return o.country.map(o2=>({id: o2.id}))
}).flat()

flatten array and put child array into an array of object

I struggled with a problem for more than an hour, how can I turn this nested array
[
[
{
"name": "1",
}
],
[
{
"name": "a",
},
{
"name": "b",
}
]
]
into this:
[
{
name: '1',
},
{
id: 'a-b',
grouped: [
{
name: 'a',
},
{
name: 'b',
},
],
},
]
I don't mind using lodash. Not sure should I flatten it before anything else would make things easier.
You could use map() to form the id and grab the parts needed to reconstruct the new array.
const data = [
[{
"name": "1",
}],
[{
"name": "a",
},
{
"name": "b",
}
]
];
const result = [
...data[0],
{
id: data[1].map(r => r.name).join("-"),
grouped: data[1]
}
];
console.log(result);
to flatten the array is a good start. That will remove the superfluous dimension from the rawArray:
const newArray = array.flat()
Now you have an array with three simple objects. The first will remain unchanged. The second element of your finalArray needs to be an object, so let's create it:
const obj = {}
the obj has two keys: id and grouped. The property of id is a string that we can create like this:
obj.id = newArray[1].name + "-" + newArray[2].name
the property of grouped remains the same:
obj.grouped = array[1]
so the finalArray is now straight forward:
const finalArray = [ newArray[0], obj ]
Put it all together in a function:
const rawArray1 = [
[
{
"name": "1a",
}
],
[
{
"name": "a",
},
{
"name": "b",
}
]
]
const rawArray2 = [
[
{
"name": "1b",
}
],
[
{
"name": "aa",
},
{
"name": "bb",
}
]
]
transformArray( rawArray1 )
transformArray( rawArray2 )
function transformArray( array ){
const newArray = array.flat()
const obj = {}
obj.id = newArray[1].name + "-" + newArray[2].name
obj.grouped = array[1]
const finalArray = [ newArray[0], obj ]
console.log(finalArray)
return finalArray
}
I managed to solve it using simple forEach, push, and flat. It's more simple than I thought, I was confused and stuck with map and reduce.
let result = [];
[
[{
"name": "1",
}],
[{
"name": "a",
},
{
"name": "b",
}
]
].forEach((val) => {
const [{
name
}] = val
if (val.length === 1) {
result.push({
name,
})
} else if (val.length > 1) {
result.push({
id: val.map(val2 => val2.name).join('-'),
grouped: val
})
}
})
console.log(result.flat())
const array1 = [
[{ name: "1" }],
[
{ name: "a" },
{ name: "b" }
]
]
const array2 = [
[{ name: "2" }],
[
{ name: "aa" },
{ name: "bb" },
{ name: "cc" }
]
]
transformArray( array1 )
transformArray( array2 )
function transformArray( array ){
const result = []
// destructure first array element for the first object:
const [ nameObj ] = array[0]
result.push( nameObj )
// map each object of the second array element into an
// an array of names, and then join the names together:
const dataObj = {}
dataObj.id = array[1].map(obj => obj.name).join('-')
dataObj.grouped = array[1]
result.push( dataObj )
console.log( result )
return result
}

Delete main array when nested array is empty

I got the following array:
var arr = [{
"mainId": 1,
"parents": [{
"parent": 1
},
{
"parent": 2
}
]
},
{
"mainId": 2,
"parents": [{
"parent": 3
}]
}
]
I'm using this function to delete an specific parent in the parents array
var idToDelete = 2
arr.forEach(function (o) {
o.parents = o.parents.filter(s => s.id !== idToDelete
})
This is working fine. But when idToDelete = 3 I want to delete the complete main Item and start a function "startSomething"
So that I'm left with the following output
var arr = [{
"mainId": 1,
"parents": [{
"parent": 1
},
{
"parent": 2
}
]
}]
How could this be implemented?
You can map and then filter the top level array, here is an example:
var arr = [{
"mainId": 1,
"parents": [{
"parent": 1
},
{
"parent": 2
}
]
},
{
"mainId": 2,
"parents": [{
"parent": 3
}]
}
];
var idToDelete = 3;
arr = arr.map((v) => ({
...v,
parents: v.parents.filter(({
parent
}) => parent !== idToDelete)
}))
.filter((v) => v.parents.length);
console.log(arr);
Filter the parent array by whether it has a .length after mutating the children. Also make sure to use the .parent property (your objects have no .id property):
const startSomething = () => console.log('startSomething');
var arr =
[
{
"mainId": 1,
"parents": [
{
"parent": 1
},
{
"parent": 2
}
]
},
{
"mainId": 2,
"parents": [
{
"parent": 3
}
]
}
];
var idToDelete = 3;
arr.forEach(function (o) {
o.parents = o.parents.filter(s => s.parent !== idToDelete)
});
const newArr = arr.filter(({ parents }) => parents.length);
console.log(newArr);
if (newArr.length !== arr.length) {
startSomething();
}
You can use .filter() on the array itself (to remove the main object) with .some() to return true or false depending on whether it should be deleted or not:
const arr = [{ "mainId": 1, "parents": [{ "parent": 1 }, { "parent": 2 } ] }, { "mainId": 2, "parents": [{ "parent": 3 }] } ];
const removeObj = (arr, id, cb) => {
const res = arr.filter(({parents}) => !parents.some(({parent}) => parent === id));
const item_removed = res.length !== arr.length;
return (item_removed && cb(res), item_removed);
}
const startSomething = new_arr => console.log("Starting with arr", new_arr);
removeObj(arr, 3, startSomething);
.as-console-wrapper { max-height: 100% !important;} /* ignore */
Here's a alternative method, with a function that uses findIndex to find the object in the array.
Then splices the object if it only has 1 parent.
Or splices the parent from object if it has more than 1 parent.
Example snippet:
const deleteParent = (arr, id) =>
{
let idx = arr.findIndex(x=>x.parents.some(p=> p.parent === id));
if (idx >= 0 && arr[idx].parents.length === 1 ) {
let obj = arr[idx];
// remove object from array and do something
arr.splice(idx, 1);
doSomething(obj);
}
else if(idx >= 0){
let obj = arr[idx];
let parentIdx = obj.parents.findIndex(x=>x.parent==id);
// remove parent
obj.parents.splice( parentIdx, 1);
}
}
function doSomething (obj) { console.log(`Deleted mainId ${obj.mainId}`) }
var arr = [
{
"mainId": 1,
"parents": [
{
"parent": 1
},
{
"parent": 2
}
]
},
{
"mainId": 2,
"parents": [
{
"parent": 3
}
]
}
];
deleteParent(arr, 3);
console.log(JSON.stringify(arr));
deleteParent(arr, 2);
console.log(JSON.stringify(arr));

sorting array object based on other array

i am creating a table based on the list tablelist given,where i am creating my table header from tablefield,but i want my header to be ordered according to tableordering property
var tablelist = {
"member": {
"name": "Richie",
"id": 5
},
"submission_time": "10/03/2018 00:00:00",
"tablefield": [
{
"field_name": "top1",
"value": 1,
},
{
"field_name": "top5",
"value": 5,
},
{
"field_name": "top3",
"value": 3,
},
{
"field_name": "top2",
"value": 2,
},
{
"field_name": "top4",
"value": 4,
},
],
"tableordering": [
"member",
"top1",
"top2",
"top3",
"top4",
"top5",
"submission_time",
]
}
i want my list result to be like
var result = [{member:"Richie",top1:"1",top2:"1",top3:"1",top4:"1",top5:"1",submission-time:"1"}]
below is the code
var lists = tablelist.reduce((acc, cur) => {
acc[cur.field_name] = cur.value;
return acc;
}, {});
var listres = Object.assign({}, lists, {
member: i.member.name,
submission_time: i.submission_time
});
but then sorting with tableordering, i do not know,could someone help
🏴󠁡󠁦󠁷󠁡󠁲󠁿 Divide and rule or reduce and map:
const tablelist = {
member: {name: `Richie`, id: 5},
submission_time: `10/03/2018 00:00:00`,
tablefield: [
{field_name: `top1`, value: 1},
{field_name: `top5`, value: 5},
{field_name: `top3`, value: 3},
{field_name: `top2`, value: 2},
{field_name: `top4`, value: 4}
],
tableordering: [
`member`,
`top1`,
`top2`,
`top3`,
`top4`,
`top5`,
`submission_time`
]
}
const {member: {name: member}, submission_time} = tablelist
const fields = tablelist.tablefield.reduce((list, {field_name, value}) => {
list[field_name] = value
return list
}, {})
const data = {member, submission_time, ...fields}
const result = [tablelist.tableordering.reduce((list, key) => {
list[key]= data[key]
return list
}, {})]
console.log(result)

How to flatten nested array of object using es6

I have this array of objects, within it I have another array of objects:
[
{
id: 1,
country: [
{
id: "5a60626f1d41c80c8d3f8a85"
},
{
id: "5a6062661d41c80c8b2f0413"
}
]
},
{
id: 2,
country: [
{
id: "5a60626f1d41c80c8d3f8a83"
},
{
id: "5a60626f1d41c80c8d3f8a84"
}
]
}
];
How to get flat array of country like this:
[
{ id: "5a60626f1d41c80c8d3f8a85" },
{ id: "5a6062661d41c80c8b2f0413" },
{ id: "5a60626f1d41c80c8d3f8a83" },
{ id: "5a60626f1d41c80c8d3f8a84" }
];
without using a forEach and a temp variable?
When I did:
(data || []).map(o=>{
return o.country.map(o2=>({id: o2.id}))
})
I got the same structure back.
Latest edit
All modern JS environments now support Array.prototype.flat and Array.prototype.flatMap
const data=[{id:1,country:[{id:"5a60626f1d41c80c8d3f8a85"},{id:"5a6062661d41c80c8b2f0413"}]},{id:2,country:[{id:"5a60626f1d41c80c8d3f8a83"},{id:"5a60626f1d41c80c8d3f8a84"}]}];
console.log(
data.flatMap(
(elem) => elem.country
)
)
Old answer
No need for any ES6 magic, you can just reduce the array by concatenating inner country arrays.
const data=[{id:1,country:[{id:"5a60626f1d41c80c8d3f8a85"},{id:"5a6062661d41c80c8b2f0413"}]},{id:2,country:[{id:"5a60626f1d41c80c8d3f8a83"},{id:"5a60626f1d41c80c8d3f8a84"}]}];
console.log(
data.reduce(
(arr, elem) => arr.concat(elem.country), []
)
)
If you want an ES6 feature (other than an arrow function), use array spread instead of the concat method:
const data=[{id:1,country:[{id:"5a60626f1d41c80c8d3f8a85"},{id:"5a6062661d41c80c8b2f0413"}]},{id:2,country:[{id:"5a60626f1d41c80c8d3f8a83"},{id:"5a60626f1d41c80c8d3f8a84"}]}];
console.log(
data.reduce(
(arr, elem) => [...arr, ...elem.country], []
)
)
Note: These suggestions would create a new array on each iteration.
For efficiency, you have to sacrifice some elegance:
const data=[{id:1,country:[{id:"5a60626f1d41c80c8d3f8a85"},{id:"5a6062661d41c80c8b2f0413"}]},{id:2,country:[{id:"5a60626f1d41c80c8d3f8a83"},{id:"5a60626f1d41c80c8d3f8a84"}]}];
console.log(
data.reduce(
(arr, elem) => {
for (const c of elem.country) {
arr.push(c);
}
return arr;
}, []
)
)
const raw = [
{
id: 1,
country: [
{
id: "5a60626f1d41c80c8d3f8a85"
},
{
id: "5a6062661d41c80c8b2f0413"
}
]
},
{
id: 2,
country: [
{
id: "5a60626f1d41c80c8d3f8a83"
},
{
id: "5a60626f1d41c80c8d3f8a84"
}
]
}
];
const countryIds = raw
.map(x => x.country)
.reduce((acc, curr) => {
return [
...acc,
...curr.map(x => x.id)
];
}, []);
console.log(countryIds)
This, works, just concat the nested arrays returned by your solution
let arr = [{ "id": 1,
"country": [{
"id": "5a60626f1d41c80c8d3f8a85",
},
{
"id": "5a6062661d41c80c8b2f0413",
}
]
},
{
"id": 2,
"country": [{
"id": "5a60626f1d41c80c8d3f8a83",
},
{
"id": "5a60626f1d41c80c8d3f8a84",
}
]
}
];
//If you want an array of country objects
console.log([].concat.apply(...(arr || []).map(o=> o.country)))
//If you can an array od country ids
console.log([].concat.apply(...(arr || []).map(o=> o.country.map(country => country.id))))
Ayush Gupta's solution will work for this case. But I would like to provide other solution.
const arr = [
{
id: 1,
country: [
{
id: '5a60626f1d41c80c8d3f8a85'
},
{
id: '5a6062661d41c80c8b2f0413'
}
]
},
{
id: 2,
country: [
{
id: '5a60626f1d41c80c8d3f8a83'
},
{
id: '5a60626f1d41c80c8d3f8a84'
}
]
}
];
const ids = arr.reduce(
(acc, {country}) => [
...acc,
...country.map(({id}) => ({
id
}))
],
[]
);
console.log(ids);
For JSON string data, it can be done during parsing too :
var ids = [], json = '[{"id":1,"country":[{"id":"5a60626f1d41c80c8d3f8a85"},{"id":"5a6062661d41c80c8b2f0413"}]},{"id":2,"country":[{"id":"5a60626f1d41c80c8d3f8a83"},{"id":"5a60626f1d41c80c8d3f8a84"}]}]';
JSON.parse(json, (k, v) => v.big && ids.push(v));
console.log( ids );
I am not sure why noone mentioned flat() (probably for large arrays, it might be less performant)
(data || []).map(o=>{
return o.country.map(o2=>({id: o2.id}))
}).flat()

Categories