We have some JSON data where each parent array object can have a property that is an array of "children" along the lines of the following :
data: [
{
value: 1,
parent_id: null,
label: "1.0 - TT One",
children: [
{
value: 3,
label: "1.1 - TT One-One",
parent_id: 1,
},
{
value: 4,
label: "1.2 - TT One-Two",
parent_id: 1,
}
]
},
{
value: 2,
parent_id: null,
label: "2.0 - TT Two",
children: [
{
value: 5,
label: "2.1 - TT Two-One",
parent_id: 2,
}
]
}
]
We'd like to "flatten" the children so that we end up with one array that is all the parents and children as follows (it does not have to stay named data if not efficient):
data: [
{
value: 1,
parent_id: null,
label: "1.0 - TT One"
},
{ <-- FORMER CHILD
value: 3,
label: "1.1 - TT One-One",
parent_id: 1
},
{ <-- FORMER CHILD
value: 4,
label: "1.2 - TT One-Two",
parent_id: 1,
},
{
value: 2,
parent_id: null,
label: "2.0 - TT Two"
},
{ <-- FORMER CHILD
value: 5,
label: "2.1 - TT Two-One",
parent_id: 2,
}
]
Thoughts on how best to accomplish this in an efficient manor? We have underscore if that will help.
Found a buttload of array to array flatten and some object to array flattens, but nothing combining the two. If we had this, we wouldn't have needed to post. If it's not obvious from exactly what we have to exactly what we need, what is the point?
the point is to understand what is your data structure and how to visit every element of that data structure.
Since there are only 2 levels you do not even need to find a general solution to transform your initial data into an array. Do you know how to traverse an array? then you know how to traverse an element that has an array as property as well.
Anyway here is both a recursive and a non recursive generalized solution.
var d = [{
value: 1,
parent_id: null,
label: "1.0 - TT One",
children: [{
value: 3,
label: "1.1 - TT One-One",
parent_id: 1,
},
{
value: 4,
label: "1.2 - TT One-Two",
parent_id: 1,
}
]
},
{
value: 2,
parent_id: null,
label: "2.0 - TT Two",
children: [{
value: 5,
label: "2.1 - TT Two-One",
parent_id: 2,
}]
}
];
function walkRecursive(data) {
let result = []
data.forEach(element => {
let e = { ...element}
result.push(e);
if (e.children) {
let children = walkRecursive(e.children)
result = result.concat(children)
delete e.children
}
});
return result;
}
function walk(data) {
data = data.slice()
let result = []
let d, oldData;
while (d = data.shift()) {
let el = { ...d}
result.push(el)
if (el.children) {
oldData = data
data = el.children.slice();
delete el.children;
} else {
if (oldData && data.length == 0) {
data = oldData
oldData = null;
}
}
}
return result;
}
console.log(walkRecursive(d))
console.log(walk(d))
https://codeburst.io/learn-and-understand-recursion-in-javascript-b588218e87ea
Related
I got an array and want to convert my array into new shape, something like what on html section, so I did it successfully, but there is a problem, and a question:
Question: can I use iteration once? I mean already used map with filter, is there a better way to do this? like use single function like filter or reduce without map ?
Problem: I want to change id key name to value and title to label, but looks like can't do this in filter() Also I used forEach and map and reduce and etc.. but didn't work.
let data = [{
id: 1,
group: 1,
parent: 1,
title: 'group a'
}, {
id: 2,
group: 1,
parent: null,
title: 'a1'
}, {
id: 3,
group: 1,
parent: null,
title: 'a2'
}, {
id: 4,
group: 2,
parent: null,
title: 'b1'
},
{
id: 5,
group: 2,
parent: 2,
title: 'group b'
}
];
let array = [];
data.map(function(item) {
if (item.parent) {
let obj = {};
obj.label = item.title
//obj.options = data.filter(x => !x.parent && x.group === item.parent);
obj.options = data.map(x => !x.parent && x.group === item.parent ? {value: x.id, label: x.title} : {});
array.push(obj)
}
})
console.log(array);
<script>
[{
label: "group a",
options: [{
label: "a1",
value: 1
},
{
label: "a2",
value: 2
}
]
},
{
label: "group b",
options: [{
label: "b1",
value: 4
}
]
}];
</script>
I tried this, and used short if condition but it will add empty object if condition is false:
obj.options = data.map(x => !x.parent && x.group === item.parent ? {value: x.id, label: x.title} : {});
Your solution is not the ideal way to do this but if tweaked a little it can still be made to work. Just put a filter after your map condition to remove all the empty objects.
let data = [{
id: 1,
group: 1,
parent: 1,
title: 'group a'
}, {
id: 2,
group: 1,
parent: null,
title: 'a1'
}, {
id: 3,
group: 1,
parent: null,
title: 'a2'
}, {
id: 4,
group: 2,
parent: null,
title: 'b1'
},
{
id: 5,
group: 2,
parent: 2,
title: 'group b'
}
];
let array = [];
data.forEach(function(item) {
if (item.parent) {
let obj = {};
obj.label = item.title
obj.options = data.map(x => !x.parent && x.group === item.parent ? {value: x.id, label: x.title} : {}).filter(x => Object.keys(x).length > 0);
array.push(obj)
}
})
console.log(array);
<script>
[{
label: "group a",
options: [{
label: "a1",
value: 1
},
{
label: "a2",
value: 2
}
]
},
{
label: "group b",
options: [{
label: "b1",
value: 4
}
]
}];
</script>
I have used Object.keys.
Also map is when you want to transform the array into a new one. map returns an array, if you are not making use of the returned array, then map is not the ideal choice. That is why I used forEach.
Note:. The way to do this properly would be .reduce(). It is for computing a result from an array, where at each iteration you can refer to the accumulated object uptil that iteration.
i have question about array, for example i have an array of object, like this
const data = [
{
level: 1,
name: "Naruto",
},
{
level: 2,
name: "Dragon",
},
{
level: 2,
name: "Ball",
},
{
level: 3,
name: "Sasuke",
},
]
Now i want to create new array base on level , mean that after format it will look like this:
[
[
{
name: "naruto"
}
],
[
{
name: "Ball"
},
{ name: "Dragon" }
],
[
{name:"Sasuke"}
]
];
How can i do this, thanks you guys
I used a loop to handle your case
currentLevel - 1 is mapped with the index of the result's array
const data = [{
level: 1,
name: "Naruto",
},
{
level: 2,
name: "Dragon",
},
{
level: 2,
name: "Ball",
},
{
level: 3,
name: "Sasuke",
},
];
let result = []
for (const currentValue of data) {
const currentLevel = currentValue.level
if (!result[currentLevel - 1]) {
result[currentLevel - 1] = []
}
result[currentLevel - 1].push({
name: currentValue.name
})
}
//it's followed by index, the result will have some `undefined` if your levels have gaps. You can comment it out, if you want to keep `undefined` values
result = result.filter(item => item)
console.log({
result
})
I'm trying to obtain a list from a list in Javascript.
This is the list:
const cars = [
{
id: 1,
brand: "Mercedes Benz",
properties: [
{
property: "Mechanical",
value: 2,
},
{
property: "Chemical",
value: 2,
},
{
property: "Pressure",
value: 3,
}],
},
{
id: 2,
brand: "BMW",
properties: [
{
property: "Mechanical",
value: 5,
},
{
property: "Chemical",
value: 3,
},
{
property: "Pressure",
value: 6,
}],
}
]
I need the cars which have a determined property with a value greater than X
For example, if I say that the property is Mechanical and the value is 3 I'll obtain the complete object with id 2
Does anyone have an idea? That is having me a hard time
Sorry for the bad formatting of the code, StackOverflow is not letting post it formatted for some reason.
Tip: I paste it on a Node REPL ;)
Thanks in advance
Use filter:
const desiredProp = "Mechanical";
const minVal = 3;
const filteredCars = cars.filter(car =>
car.properties.some(
prop => prop.property === desiredProp && prop.value >= minVal
)
);
If you're only looking for the first match and not an array of all matches, replace filter with find.
You can create a propGreaterThan function that takes the desired property and the number that you need it to be greater then. Using a combination of filter on the cars array and find on the properties array, you can find the appropriate cars.
const cars = [
{
id: 1,
brand: "Mercedes Benz",
properties: [
{
property: "Mechanical",
value: 2,
},
{
property: "Chemical",
value: 2,
},
{
property: "Pressure",
value: 3,
}],
},
{
id: 2,
brand: "BMW",
properties: [
{
property: "Mechanical",
value: 5,
},
{
property: "Chemical",
value: 3,
},
{
property: "Pressure",
value: 6,
}],
}
]
function propGreaterThan(prop, num) {
return cars.filter(car => {
return car.properties.find(p => p.property === prop).value > num;
});
}
console.log(
propGreaterThan("Chemical", 2)
)
I have 2 arrays:
0: {id: 2, name: "TMA"}
1: {id: 3, name: "Hibbernate"}
0: {id: 1, name: "FB.DE"}
1: {id: 2, name: "TMA"}
2: {id: 3, name: "Hibbernate"}
3: {id: 4, name: "Event.it A"}
4: {id: 5, name: "Projket 2"}
5: {id: 6, name: "Projekt 1"}
I want to compare them and delete the objects with the id 2 and 3 cause both arrays have them and thats the similarity.
This is my Code so far:
const projectListOutput = projectsOfPersonArray.filter(project => data.includes(project));
console.log(projectListOutput);
But every time i run this projectListOutput is empty.
When using includes dont compare objects, Just build data as array of strings. Remaining code is similar to what you have.
arr1 = [
{ id: 2, name: "TMA" },
{ id: 3, name: "Hibbernate" },
];
arr2 = [
{ id: 1, name: "FB.DE" },
{ id: 2, name: "TMA" },
{ id: 3, name: "Hibbernate" },
{ id: 4, name: "Event.it A" },
{ id: 5, name: "Projket 2" },
{ id: 6, name: "Projekt 1" },
];
const data = arr1.map(({ id }) => id);
const result = arr2.filter(({ id }) => !data.includes(id));
console.log(result);
Your data array probably does not contain the exact same object references than projectsOfPersonArray. Look at the code below:
[{ foo: 'bar' }].includes({ foo: 'bar' });
// false
Objects look equal, but they don't share the same reference (= they're not the same).
It's safer to use includes with primitive values like numbers or strings. You can for example check the ids of your objects instead of the full objects.
You compare different objects, so every object is unique.
For filtering, you need to compare all properties or use a JSON string, if the order of properties is equal.
var exclude = [{ id: 2, name: "TMA" }, { id: 3, name: "Hibbernate" }],
data = [{ id: 2, name: "TMA" }, { id: 3, name: "Hibbernate" }, { id: 1, name: "FB.DE" }, { id: 2, name: "TMA" }, { id: 3, name: "Hibbernate" }, { id: 4, name: "Event.it A" }, { id: 5, name: "Projket 2" }, { id: 6, name: "Projekt 1" }],
result = data.filter(project =>
!exclude.some(item => JSON.stringify(item) === JSON.stringify(project))
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can do something similar to the next:
const source = [{
id: 1,
name: "FB.DE"
},
{
id: 2,
name: "TMA"
},
{
id: 3,
name: "Hibbernate"
},
{
id: 4,
name: "Event.it A"
},
{
id: 5,
name: "Projket 2"
},
{
id: 6,
name: "Projekt 1"
}
]
const toRemove = [{
id: 2,
name: "TMA"
},
{
id: 3,
name: "Hibbernate"
}
]
/**create object where keys is object "id" prop, and value is true**/
const toRemoveMap = toRemove.reduce((result, item) => ({
...result,
[item.id]: true
}), {})
const result = source.filter(item => !toRemoveMap[item.id])
You can make function from it:
function removeArrayDuplicates (sourceArray, duplicatesArray, accessor) {
const toRemoveMap = duplicatesArray.reduce((result, item) => ({
...result,
[item[accessor]]: true
}), {});
return sourceArray.filter(item => !toRemoveMap[item[accessor]])
}
removeArrayDuplicates(source, toRemove, 'id')
Or even better, you can make it work with a function instead of just property accessor:
function removeDuplicates (sourceArray, duplicatesArray, accessor) {
let objectSerializer = obj => obj[accessor];
if(typeof accessor === 'function') {
objectSerializer = accessor;
}
const toRemoveMap = duplicatesArray.reduce((result, item) => ({
...result,
[objectSerializer(item)]: true
}), {});
return sourceArray.filter(item => !toRemoveMap[objectSerializer(item)])
}
removeDuplicates(source, toRemove, (obj) => JSON.stringify(obj))
This function will help you merge two sorted arrays
var arr1 = [
{ id: 2, name: 'TMA' },
{ id: 3, name: 'Hibbernate' },
]
var arr2 = [
{ id: 1, name: 'FB.DE' },
{ id: 2, name: 'TMA' },
{ id: 3, name: 'Hibbernate' },
{ id: 4, name: 'Event.it A' },
{ id: 5, name: 'Projket 2' },
]
function mergeArray(array1, array2) {
var result = []
var firstArrayLen = array1.length
var secondArrayLen = array2.length
var i = 0 // index for first array
var j = 0 // index for second array
while (i < firstArrayLen || j < secondArrayLen) {
if (i === firstArrayLen) { // first array doesn't have any other members
while (j < secondArrayLen) { // we copy rest members of first array as a result
result.push(array2[j])
j++
}
} else if (j === secondArrayLen) { // second array doesn't have any other members
while (i < firstArrayLen) { // we copy the rest members of the first array to the result array
result.push(array1[i])
i++
}
} else if (array1[i].id < array2[j].id) {
result.push(array1[i])
i++
} else if (array1[i].id > array2[j].id) {
result.push(array2[j])
j++
} else {
result.push(array1[i])
i++
j++
}
}
return result
}
console.log(mergeArray(arr1,arr2));
This question already has answers here:
JavaScript format array of objects into nested children
(3 answers)
Create nested array data from an array of objects
(5 answers)
Convert array of objects with parent / child relationships to array of nested objects
(1 answer)
Closed 3 years ago.
I have a data like this:
[
{
ID: 1,
Title: 't1',
parentID: null,
},
{
ID: 2,
Title: 't2',
parentID: null,
},
{
ID: 50,
Title: 'tt',
parentID: 2,
},
{
ID: 30,
Title: 'ct',
parentID: 2,
},
{
ID: 6,
Title: 'cyt',
parentID: 50,
}
]
I want to convert the list above to a tree like structure, which each object has a children and this child-parent relationship is defined in the list using a parentID, if the parentID is null it means the object doesn't have any parent
[
{
ID: 1,
Title: 't1',
parentID: null,
},
{
ID: 2,
Title: 't2',
parentID: null,
children: [
{
ID: 50,
Title: 'tt',
parentID: 2,
children: [
{
ID: 6,
Title: 'cyt',
parentID: 50,
}
]
},
{
ID: 30,
Title: 'ct',
parentID: 2,
},
]
}
]
I am looking for a good way to do this which is both optimal and doesn't require lots of looping, since the objects might grow in number.
You can first create a mapping of IDs to objects, and then in one pass assign all children based on this mapping. Runtime complexity O(n).
var data = [
{
ID: 1,
Title: 't1',
parentID: null,
},
{
ID: 2,
Title: 't2',
parentID: null,
},
{
ID: 50,
Title: 'tt',
parentID: 2,
},
{
ID: 30,
Title: 'ct',
parentID: 2,
},
{
ID: 6,
Title: 'cyt',
parentID: 50,
}
] ;
const map = {};
for (let i= 0; i < data.length; i++) {
map[data[i].ID] = data[i];
}
for (let i= 0; i < data.length; i++) {
const parent = map[data[i].parentID];
if (parent) {
if (parent.children == null) {
parent.children = [data[i]];
} else {
parent.children.push(data[i]);
}
}
}
console.log(data.filter(e => e.parentID == null));