I want to combine two arrays of objects, to make it easier for me to display in HTML. The function should find matching values of keys called "id" in arr1, and "source" in arr2. Here's what it looks like:
let arr1 = [
{id = 1,
name = "Anna"},
{id = 2,
name = "Chris"}
]
let arr2 = [
{childName = "Brian",
{source = 1}},
{childName = "Connie",
{source = 2}}
{childName = "Dory",
{source = 1}}
]
I tried different approaches, with best one being using forEach and filter on the arrays. I'm trying to set up a new property in arr1 objects called "children".
arr1.forEach(el => el.children = arr2.filter(checkMatch));
function checkMatch(child){
for(let i=0;i<arr1.length;i++){
child.childName.source === arr1[i].id
}
}
And this results in adding appropriate children to the first object(Anna has Brian and Dory now) so it's correct, but it also adds the same children to the second object (so Chris has also Brian and Dory).
Where is my mistake here? I'm guessing that the loop doesn't work the way I want it to work, but I don't know which one and how it happens.
Since your syntax for creating the objects of arr1 and arr2 are not valid i tried to guess the structure of your objects.
let arr1 = [
{
id: 1,
name: "Anna"
},
{
id: 2,
name: "Chris"
}
];
let arr2 = [
{
childName: "Brian",
source: 1
},
{
childName: "Connie",
source: 2
},
{
childName: "Dory",
source: 1
}
];
arr2.map((child) => {
for (let parent of arr1) {
if (parent.id == child.source) {
if (!parent.children) {
parent.children = [];
}
parent.children.push(child);
}
}
});
console.log(arr1);
There were problems with your JSON, but I tidied and here is option using map and filter
const arr1 = [{
id: 1,
name: "Anna"
},
{
id: 2,
name: "Chris"
}];
const arr2 = [{
childName: "Brian",
parent: {
source: 1
}
},
{
childName: "Connie",
parent: {
source: 2
}
},
{
childName: "Dory",
parent: {
source: 1
}
}];
let merge = arr1.map(p => {
p.children = arr2.filter(c => c.parent.source === p.id).map(c => c.childName);
return p;
});
console.log(merge);
Your json have some problems you should use
:
instead of
=
Also some Braces make the structure incorrect, but I think what you want to do here is fill a children sub array with the childNames of the subject here is my approach:
var json =
[
{
"id" : 1,
"name" : "Anna"
},
{
"id" : 2,
"name" : "Chris"
}
];
var subJson = [
{
"childName" : "Brian",
"source" : 1
},
{
"childName" : "Connie",
"source" : 2
},
{"childName" : "Dory",
"source" : 1
}
];
var newJson = [];
$.each(json,function(index1,item){
newJson.push({"id":item.id,"name":item.name, "children": []});
$.each(subJson,function(index2,subitem){
if(subitem.source == item.id){
newJson[newJson.length - 1].children.push({"childName":subitem.childName}) ;
}
})
})
console.log(newJson);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Hope it helps
The below uses Map for storage and convenient lookup of parents.
const parents = [
{
id: 1,
name: "Anna"
},
{
id: 2,
name: "Chris"
}
]
const children = [
{
childName: "Brian",
source: 1
},
{
childName: "Connie",
source: 2
},
{
childName: "Dory",
source: 1
}
]
// Create a map for easy lookup of parents.
const parentMap = new Map()
// Add parents to the map, based on their id.
parents.forEach(parent => parentMap.set(parent.id, parent))
// Add children to their parents.
children.forEach((child) => {
// Get the parent from the map.
const parent = parentMap.get(child.source)
// Handle parent not found error.
if (!parent) { return console.error('parent not found for', child.childName)}
// Create the children array if it doesn't already exist.
parent.children = parent.children || []
// Add the child to the parent's children array.
parent.children.push(child)
})
// Output the result.
Array.from(parentMap).forEach(parent => console.log(parent[1]))
Result:
{
id: 1,
name: 'Anna',
children: [
{ childName: 'Brian', source: 1 },
{ childName: 'Dory', source: 1 }
]
}
{
id: 2,
name: 'Chris',
children: [
{ childName: 'Connie', source: 2 }
]
}
Related
Let's suppose we have an array that has properties id, name, age. I want to add more properties to it like height if it is not in the existing array. And if it that property exists it should update the array element.
let array1 = [
{
id : 1,
name : "Ravi",
age : 29
},
{
id : 2,
name : "Nitin",
age : 31,
height : "5.5",
}
]
let array2 = [
{
id : 1,
height : "5.8",
},
{
id : 2,
height : "6.1",
}
]
Created a function that could first check if ID exists then update the property if doesn't exists then create it.
After that it should check it the property exists then it should update the property if not then property needs to be added to existing element having old properties.
function updateorinsert(array, item) {
const i = array.findIndex(_item => _item.id === item.id);
if (i !== -1) {
array[i] = item;
array.map((element, index) => {
let result = array[index].hasOwnProperty("height");
if (result) {
// update the property
} else {
// add that property
}
});
} else {
array.push(item);
}
}
//Calling the function :
updateorinsert(array1, {id : 1, height : "5.8",}) // this can be changes as {id : 2, height : "6.1",}
let output_array = [
{
id : 1,
name : "Ravi",
age : 29,
height : "5.8",
},
{
id : 2,
name : "Nitin",
age : 31,
height : "6.1",
}
]
You can easily achieve this result using map and spread syntax
let array1 = [
{ id: 1, name: "Ravi", age: 29 },
{ id: 2, name: "Nitin", age: 31, height: "5.5" },
];
let array2 = [
{ id: 1, height: "5.8" },
{ id: 2, height: "6.1" },
];
const result = array1.map((o) => ({
...o,
...array2.find((obj) => obj.id === o.id),
}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
It's really unnecessary to specifically compute if a value needs to be inserted or added if all you really want to do is merge arrays and overwrite any value if it's seen later.
You can place all your arrays into an array and reduce them into a single array. First create a map by id to element values, then return the array of computed values.
The idea here is to continually spread in the values matched by the id property, if the property didn't exist previously it will now, and if it did it will be overwritten.
Note: This method avoids the O(n) id search complexity of the nested array.find by building a map for O(1) constant time lookups.
Object.values([/* ...arrays */].reduce((res, current) => {
current.forEach(el => {
res[el.id] = { ...res[el.id], ...el };
});
return res;
}, {}));
const array1 = [
{ id: 1, name: "Ravi", age: 29 },
{ id: 2, name: "Nitin", age: 31, height: "5.5" },
];
const array2 = [
{ id: 1, height: "5.8" },
{ id: 2, height: "6.1" },
];
const res = Object.values([array1, array2].reduce((res, current) => {
current.forEach(el => {
res[el.id] = { ...res[el.id], ...el };
});
return res;
}, {}));
console.log(res);
// This is a large array of objects, e.g.:
let totalArray = [
{"id":"rec01dTDP9T4ZtHL4","fields":
{"user_id":170180717,"user_name":"abcdefg","event_id":516575,
}]
let uniqueArray = [];
let dupeArray = [];
let itemIndex = 0
totalArray.forEach(x => {
if(!uniqueArray.some(y => JSON.stringify(y) === JSON.stringify(x))){
uniqueArray.push(x)
} else(dupeArray.push(x))
})
node.warn(totalArray);
node.warn(uniqueArray);
node.warn(dupeArray);
return msg;
I need my code to identify duplicates in the array by a key value of user_id within the objects in the array. Right now, my code works to identify identical objects in the array, but I need it to identify dupes based on a key value inside the objects instead. How do I do this? I am struggling to figure out how to path the for each loop to identify the dupe based on the key value instead of the entire object.
Right now, my code works to identify identical objects in the array, but I need it to identify dupes based on a key value inside the objects instead. How do I do this?
Don’t compare the JSON representation of the whole objects then, but only their user_id property specifically.
totalArray.forEach(x => {
if(!uniqueArray.some(y => y.fields.user_id === x.fields.user_id)){
uniqueArray.push(x)
} else(dupeArray.push(x))
})
You could take a Set and push to either uniques or duplicates.
var array = [
{ id: 1, data: 0 },
{ id: 2, data: 1 },
{ id: 2, data: 2 },
{ id: 3, data: 3 },
{ id: 3, data: 4 },
{ id: 3, data: 5 },
],
uniques = [],
duplicates = [];
array.forEach(
(s => o => s.has(o.id) ? duplicates.push(o) : (s.add(o.id), uniques.push(o)))
(new Set)
);
console.log(uniques);
console.log(duplicates);
.as-console-wrapper { max-height: 100% !important; top: 0; }
One way is to keep a list of ids you found so far and act accordingly:
totalArray = [
{ id: 1, val: 10 },
{ id: 2, val: 20 },
{ id: 3, val: 30 },
{ id: 2, val: 15 },
{ id: 1, val: 50 }
]
const uniqueArray = []
const dupeArray = []
const ids = {}
totalArray.forEach( x => {
if (ids[x.id]) {
dupeArray.push(x)
} else {
uniqueArray.push(x)
ids[x.id] = true
}
})
for (const obj of uniqueArray) console.log("unique:",JSON.stringify(obj))
for (const obj of dupeArray) console.log("dupes: ",JSON.stringify(obj))
i have tree array of nested objects. Depending on the type of element I want to give it the necessary icon.
const treeData = [
{
id: 1,
type: "FOLDER",
children: [
{
id: 2,
type: "FILE"
},
{
id: 2,
type: "FOLDER",
children: []
},
]
}
]
Unlimited number of nesting possible in folders. Output should be like that.
const treeData = [
{
id: 1,
type: "FOLDER",
icon: "folder-icon"
children: [
{
id: 2,
type: "FILE",
icon: "file-icon"
},
{
id: 2,
type: "FOLDER",
children: []
icon: "file-icon"
},
]
}
]
As i understand i should use recursive map function with CHILDREN check. But i can't reach the proper result.
You could use map method and create a recursive function that will take type and convert it lowercase to create icon name and add it to the new object.
const data = [{"id":1,"type":"FOLDER","children":[{"id":2,"type":"FILE"},{"id":2,"type":"FOLDER","children":[]}]}]
function addIcons(data) {
return data.map(({ type, children = [], ...rest }) => {
const o = { ...rest, type }
if(type) o.icon = `${type.toLowerCase()}-icon`;
if (children.length) o.children = addIcons(children)
return o
})
}
console.log(addIcons(data))
You can create a function like this:
const iconCalculator = object => {
if (object.children) object.children = object.children.map(child=>iconCalculator(child))
return {...object, icon: 'whatever'}
}
And then map your tree like this treeData.map(child=>iconCalculator(child))
You could map by using a new object with recursive children.
const
addIcon = o => ({
...o,
icon: `${o.type.toLowerCase()}-icon`,
...(Array.isArray(o.children)
? { children: o.children.map(addIcon) }
: {}
)
}),
treeData = [{ id: 1, type: "FOLDER", children: [{ id: 2, type: "FILE" }, { id: 2, type: "FOLDER", children: [] }] }],
result = treeData.map(addIcon);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use forEach method something like this
const treeData = [{id: 1,type: "FOLDER",children: [{id: 2,type: "FILE"},{id: 2,type: "FOLDER",children: []},]}]
function addIcon(data) {
if (Array.isArray(data)) {
data.forEach(value => {
value.icon = value.type.toLowerCase() + '-icon'
if (Array.isArray(value.children)) {
addIcon(value.children)
}
})
}
}
addIcon(treeData)
console.log(treeData)
P.S:- This mutates original array if you don't want you can make a copy every time and return the newly created copy
I am trying to filter the parent, by removing it's child id only by not matching. in case if there is no child exist, the parent should be removed.
I try like this, but not works.
var rm = 7;
var objects = [
{
name: "parent1",
id: 1,
blog: [
{
name: "child1",
id: 1
},
{
name: "child2",
id: 2
}
]
},
{
name: "parent2",
id: 2,
blog: [
{
name: "child3",
id: 3
},
{
name: "child4",
id: 4
}
]
},
{
name: "parent3",
id: 3,
blog: [
{
name: "child5",
id: 5
},
{
name: "child6",
id: 6
}
]
},
{
name: "parent4",
id: 3,
blog: [
{
name: "child6",
id: 7
}
]
},
]
var result = objects.filter(value => {
if(!value.blog) return;
return value.blog.some(blog => blog.id !== rm)
})
console.log(result);
What is wrong here, or some one show me the correct approach?
looking for :
need to remove the blog if the id is same as rm, parent with other children required to exist.
need to remove the parent, after remove the children, in case there is no child(blog) exist.
Live Demo
Loop through the list of parents, and inside that loop, try to remove blogs with the given id first. Once you have done that, you can check if the blogs property has became empty, and if so, filter it out:
// We're going to filter out objects with no blogs
var result = objects.filter(value => {
// First filter blogs that match the given id
value.blog = value.blog.filter(blog => blog.id !== rm);
// Then, if the new length is different than 0, keep the parent
return value.blog.length;
})
I think the below code is what you are looking for
var result = objects.map(value => {
const blog = value.blog.filter(blog => blog.id !== rm);
if(blog.length === 0) {
return;
}
value.blog = blog;
return value;
}).filter(item => item);
Demo: https://jsfiddle.net/7Lp82z4k/3/
var result = objects.map(parent => {
parent.blog = parent.blog.filter(child => child.id !== rm);
return parent}).filter(parent => parent.blog && parent.blog.length > 0);
I have an app with a tree of nested nodes. all nodes are same type.
{
id: 1,
title: "node_1",
children: [
{
id: 2,
title: "node_2",
children: []
},
{
id: 3,
title: "node_3",
children: []
}
]
}
When user expanded some node (for example node with id === 3) i have to perform request to database and insert response (array children) inside of "children" property of node with id === 3 . So as result app state should be like this:
{
id: 1,
title: "node_1",
children: [
{
id: 2,
title: "node_2",
children: []
},
{
id: 3,
title: "node_3",
children: [
{
id: 4,
title: "node_4",
children: []
},
{
id: 5,
title: "node_5",
children: []
}
]
}
]
}
how can i paste array of children inside node_3 children property?
Given:
const layer1Id = 1;
const layer2Id = 3;
const newArray = [
{
id: 4,
title: "node_4",
children: [],
},
{
id: 5,
title: "node_5",
children: [],
}
];
Then, in the reducer you'll do:
return Object.assign({}, state, { children: state.children.map(child => {
if (child.id !== layer1Id) return child;
return Object.assign({}, child, { children: child.children.map(node => {
if (node.id !== layer2Id) return node;
return Object.assign({}, node, { children: node.children.concat(newArray) });
})});
})});
To make sure you don't mutate the previous state.
If it is dynamically or deeply nested, I'll recommend you to write some recursive function and use that instead.
Edit: here's sample recursive solution (untested). The indices are in order by level (ie: indices[0] refers to first level's id, indices[1] refers to second level's id):
const state = { ... };
const indices = [1, 3, 4, 5, 6];
const newArray = [ ... ];
const recursion = (node, ids, newChildren) => {
let children;
if (ids.length === 0) {
children = newChildren;
} else {
children = node.children.map(child => {
if (child.id !== ids[0]) return child;
return Object.assign({}, child, { children: recursion(child, ids.slice(1), newChildren) });
});
}
return Object.assign({}, node, { children });
};
recursion(state, indecies, newArray);
The suggested approach for relational or normalized data in a Redux store is to organize it in "normalized" fashion, similar to database tables. That will make it easier to manage updates. See http://redux.js.org/docs/FAQ.html#organizing-state-nested-data, How to handle tree-shaped entities in Redux reducers?, and https://github.com/reactjs/redux/pull/1269.
Just iterate through children array and push to correct one .
var id = expandedItemId;
for(var i = 0; i < obj.children.length; i++){
if(obj.id == expandedItemId){
obj.children.push(`data got from server`);
}
}