Related
I am attempting to build a new object from an existing deep nested object. I can't seem to get my mind in recurive mode but I am running into a bit of trouble:
oldObjArr = [{
id:1,
name:"Record1"
},{
id:2,
name:"Record2"
},{
id:3,
name:"Record3",
kids:[{
id: 4,
name: "Child 3-1"
},{
id: 5,
name: "Child 3-2"
}]
}]
buildTreeNodes = (node) => {
let data = []
node.map(record=>{
record["icon"] = "..."
record["color"] = "..."
data.push(record)
record.kids && buildTreeNodes(record.kids)
})
}
let newObjArr = buildTreeNodes(oldObjArr)
This OBVIOUSLY does not work, but I can't figure out what will. The resulting object should look like this:
[{
id:1,
name:"Record1",
icon:"...",
color: "...",
},{
id:2,
name:"Record2",
icon:"...",
color: "...",
},{
id:3,
name:"Record3",
icon:"...",
color: "...",
kids:[{
id: 4,
name: "Child 3-1",
icon:"...",
color: "...",
},{
id: 5,
name: "Child 3-2",
icon:"...",
color: "...",
}]
}]
Thanks for any help.
Robert's answer is correct.
If by chance you also want to not mutate the original object, then you can do something like this.
Also using ES6 features coz why not.
const oldObjArr = [{
id: 1,
name: "Record1"
}, {
id: 2,
name: "Record2"
}, {
id: 3,
name: "Record3",
kids: [{
id: 4,
name: "Child 3-1"
}, {
id: 5,
name: "Child 3-2"
}]
}];
function transformObject(item) {
if (Array.isArray(item.kids))
return {
...item, icon: '...', color: '...',
kids: item.kids.map(transformObject)
};
else
return {...item, icon: '...', color: '...' };
}
const newArray = oldObjArr.map(transformObject);
console.log(newArray);
So you iterate over you array and take each object and then add your props to it. Then you check if kids exist and some check if is array. i use instanceof but like #Heretic Monkey point it can be Array.isArray. What more you can setup type guard on front of function check that array argument is array then this you don't have to check that if kids is type of array.
const oldObjArr = [{
id:1,
name:"Record1"
},{
id:2,
name:"Record2"
},{
id:3,
name:"Record3",
kids:[{
id: 4,
name: "Child 3-1"
},{
id: 5,
name: "Child 3-2"
}]
}]
const addKeys = arr => {
for(const obj of arr){
obj['icon'] = "test"
obj['color'] = "test"
if("kids" in obj && obj.kids instanceof Array){
addKeys(obj.kids);
}
}
}
addKeys(oldObjArr)
console.log(oldObjArr)
V2
const addKeys = arr => {
if(!Array.isArray(arr))
return;
for(const obj of arr){
if(typeof obj !== "object")
continue;
obj['icon'] = "test"
obj['color'] = "test"
if("kids" in obj){
addKeys(obj.kids);
}
}
}
Ok check this out:
buildTreeNodes = (node) => {
let data = node.map(record=>{
record["icon"] = "..."
record["color"] = "..."
if (record.kids) record.kids = buildTreeNodes(record.kids);
return record;
})
return data;
}
let newObjArr = buildTreeNodes(oldObjArr)
console.log(newObjArr)
I think this is what you were after. You have to return record with each iteration of map, and it will add it directly to data array. The recursion within works the same.
All details are commented in demo below
let objArr = [{
id: 1,
name: "Record 1"
}, {
id: 2,
name: "Record 2"
}, {
id: 3,
name: "Record 3",
kids: [{
id: 4,
name: "Child 3-1"
}, {
id: 5,
name: "Child 3-2"
}]
},
/*
An object with a nested object not in an array
*/
{
id: 6,
name: 'Record 6',
kid: {
id: 7,
name: 'Child 6-1'
}
},
/*
An object that's filtered out because it doesn't have 'id' key/property
*/
{
no: 0,
name: null
},
/*
An object that's filtered out because it doesn't have 'id' key/property BUT has a nested object that has 'id'
*/
{
no: 99,
name: 'Member 99',
kid: {
id: 8,
name: 'Scion 99-1'
}
}
];
/*
Pass an object that has the key/value pairs that you want added to other objects
*/
const props = {
icon: '...',
color: '...'
};
/*
Pass...
a single object: {obj} of objArr[]
a single key/property: 'id'
an object that contains the key/value pairs to be added to each object that has key/property of id: {props}
*/
const addProp = (obj, prop, keyVal) => {
/*
Convert {props} object into a 2D array
props = {icon: '...', color: '...'}
~TO~
kvArr = [['icon', '...'], ['color', '...']]
*/
let kvArr = Object.entries(keyVal);
/*
for Each key/value pair of kvArr[][]
assign them to the (obj} if it has ['prop']
as one of it's key/properties
(in this demo it's 'id')
*/
kvArr.forEach(([key, val]) => {
if (obj[prop]) {
obj[key] = val;
}
});
/*
Convert {obj} into a 2D array
obj = {id: 3, name: "Record 3", kids: [{ id: 4, name: "Child 3-1"}, {id: 5, name: "Child 3-2"}]}
~TO~
subArr = [['id', 3], ['name', "Record 3"], ['kids', [{id: 4, name: "Child 3-1"}, {id: 5, name: "Child 3-2"}]]
*/
let subArr = Object.entries(obj);
/*
for Each value of subArr[][] (ie ['v'])
if it's an [Array] call addProp and pass
the {obj} of subArr[][]
*/
/*
if it's an {obj} do the same as above
*/
subArr.forEach(([k, v]) => {
if (Array.isArray(v)) {
v.forEach(subObj => {
addProp(subObj, prop, keyVal);
});
} else if (v instanceof Object) {
addProp(v, prop, keyVal);
}
});
};
// Run addProp() on each {obj} of objArr[]
for (let object of objArr) {
addProp(object, 'id', props);
}
console.log(JSON.stringify(objArr, null, 2));
I have a array of objects. I want to update an object using id.
I am able to do using the map function. Is there an alternative way or more efficient way to update the array?
Here is my code:
https://stackblitz.com/edit/js-xgfwdw?file=index.js
var id = 3
var obj = {
name: "test"
}
let arr = [{
name: "dd",
id: 1
}, {
name: "dzxcd",
id: 3
}, {
name: "nav",
id: 5
}, {
name: "hhh",
id: 4
}]
function getUpdated(obj, id) {
var item = [...arr];
const t = item.map((i) => {
if(i.id==id){
return {
...obj,
id
}
}else {
return i;
}
})
return t
}
console.log(getUpdated(obj,id))
The expected output is correct but I want to achieve the same functionality using an alternative way.
[{
name: "dd",
id: 1
}, {
name: "test",
id: 3
}, {
name: "nav",
id: 5
}, {
name: "hhh",
id: 4
}]
you are in the correct way, basically the bad thing that you are doing is creating new arrays [...arr], when map already gives you a new array.
other things to use, may be the ternary operator and return directly the result of the map function
check here the improvedGetUpdate:
var id = 3;
var obj = {
name: "test"
};
let arr = [{
name: "dd",
id: 1
}, {
name: "dzxcd",
id: 3
}, {
name: "nav",
id: 5
}, {
name: "hhh",
id: 4
}]
function getUpdated(obj, id) {
var item = [...arr];
const t = item.map((i) => {
if (i.id == id) {
return {
...obj,
id
}
} else {
return i;
}
})
return t
}
improvedGetUpdate = (obj, id) => arr.map(i => {
return i.id !== id ? i : {
...obj,
id
}
})
console.log(getUpdated(obj, id))
console.log(improvedGetUpdate(obj, id))
var id = 3
var obj = {
name: "test"
}
let arr = [{
name: "dd",
id: 1
}, {
name: "dzxcd",
id: 3
}, {
name: "nav",
id: 5
}, {
name: "hhh",
id: 4
}]
const result = arr.map((el) => el.id === id ? {...obj, id} : el)
console.log(result);
Use splice method which can be used to update the array too:
var obj = {
id: 3,
name: "test"
}
let arr = [{
name: "dd",
id: 1
}, {
name: "dzxcd",
id: 3
}, {
name: "nav",
id: 5
}, {
name: "hhh",
id: 4
}]
arr.splice(arr.findIndex(({id}) => id === obj.id), 0, obj);
console.log(arr);
#quirimmo suggested short code.
I suggest fast code.
var id = 3;
var obj = {
id: 3,
name: "test"
}
let arr = [{
name: "dd",
id: 1
}, {
name: "dzxcd",
id: 3
}, {
name: "nav",
id: 5
}, {
name: "hhh",
id: 4
}]
var arr2 = [...arr];
console.time('⏱');
arr.splice(arr.findIndex(({id}) => id === obj.id), 0, obj);
console.timeEnd('⏱');
console.time('⏱');
for (let item of arr2) {
if (item.id === id) {
item.name = obj.name;
break;
}
}
console.timeEnd('⏱');
console.log(arr2);
This question already has answers here:
How to get the difference between two arrays of objects in JavaScript
(22 answers)
Closed 1 year ago.
I need some help. How can I get the array of the difference on this scenario:
var b1 = [
{ id: 0, name: 'john' },
{ id: 1, name: 'mary' },
{ id: 2, name: 'pablo' },
{ id: 3, name: 'escobar' }
];
var b2 = [
{ id: 0, name: 'john' },
{ id: 1, name: 'mary' }
];
I want the array of difference:
// [{ id: 2, name: 'pablo' }, { id: 3, name: 'escobar' }]
How is the most optimized approach?
I´m trying to filter a reduced array.. something on this line:
var Bfiltered = b1.filter(function (x) {
return x.name !== b2.reduce(function (acc, document, index) {
return (document.name === x.name) ? document.name : false
},0)
});
console.log("Bfiltered", Bfiltered);
// returns { id: 0, name: 'john' }, { id: 2, name: 'pablo' }, { id: 3, name: 'escobar' } ]
Thanks,
Robot
.Filter() and .some() functions will do the trick
var b1 = [
{ id: 0, name: 'john' },
{ id: 1, name: 'mary' },
{ id: 2, name: 'pablo' },
{ id: 3, name: 'escobar' }
];
var b2 = [
{ id: 0, name: 'john' },
{ id: 1, name: 'mary' }
];
var res = b1.filter(item1 =>
!b2.some(item2 => (item2.id === item1.id && item2.name === item1.name)))
console.log(res);
You can use filter to filter/loop thru the array and some to check if id exist on array 2
var b1 = [{ id: 0, name: 'john' }, { id: 1, name: 'mary' }, { id: 2, name: 'pablo' }, { id: 3, name: 'escobar' } ];
var b2 = [{ id: 0, name: 'john' }, { id: 1, name: 'mary' }];
var result = b1.filter(o => !b2.some(v => v.id === o.id));
console.log(result);
Above example will work if array 1 is longer. If you dont know which one is longer you can use sort to arrange the array and use reduce and filter.
var b1 = [{ id: 0, name: 'john' }, { id: 1, name: 'mary' }, { id: 2, name: 'pablo' }, { id: 3, name: 'escobar' } ];
var b2 = [{ id: 0, name: 'john' }, { id: 1, name: 'mary' }];
var result = [b1, b2].sort((a,b)=> b.length - a.length)
.reduce((a,b)=>a.filter(o => !b.some(v => v.id === o.id)));
console.log(result);
Another possibility is to use a Map, allowing you to bring down the time complexity to O(max(n,m)) if dealing with a Map-result is fine for you:
function findArrayDifferences(arr1, arr2) {
const map = new Map();
const maxLength = Math.max(arr1.length, arr2.length);
for (let i = 0; i < maxLength; i++) {
if (i < arr1.length) {
const entry = arr1[i];
if (map.has(entry.id)) {
map.delete(entry.id);
} else {
map.set(entry.id, entry);
}
}
if (i < arr2.length) {
const entry = arr2[i];
if (map.has(entry.id)) {
map.delete(entry.id);
} else {
map.set(entry.id, entry);
}
}
}
return map;
}
const arr1 = [{id:0,name:'john'},{id:1,name:'mary'},{id:2,name:'pablo'},{id:3,name:'escobar'}];
const arr2 = [{id:0,name:'john'},{id:1,name:'mary'},{id:99,name:'someone else'}];
const resultAsArray = [...findArrayDifferences(arr1,arr2).values()];
console.log(resultAsArray);
I have an array of objects, each with an 'id' and a 'name'. I'm retrieving an 'id' from the server and need to reorder the array starting from this id.
Example code:
var myList = [
{
id: 0,
name: 'Joe'
},
{
id: 1,
name: 'Sally'
},
{
id: 2,
name: 'Chris'
},
{
id: 3,
name: 'Tiffany'
},
{
id: 4,
name: 'Kerry'
}
];
Given an 'id' of 2, how can I reorder the array so my output is as follows:
var newList = [
{
id: 2,
name: 'Chris'
},
{
id: 3,
name: 'Tiffany'
},
{
id: 4,
name: 'Kerry'
},
{
id: 0,
name: 'Joe'
},
{
id: 1,
name: 'Sally'
}
];
Try this:
function orderList(list, id){
return list.slice(id).concat(list.slice(0,id));
}
Link to demo
You could slice the array at given index and return a new array using spread syntax.
const myList = [{id:0,name:'Joe'},{id:1,name:'Sally'},{id:2,name:'Chris'},{id:3,name:'Tiffany'},{id:4,name:'Kerry'}];
const slice = (arr, num) => [...arr.slice(num), ...arr.slice(0, num)];
console.log(slice(myList, 2));
myList.sort(function(a,b){
return a.id>2===b.id>2?a.id-b.id:b.id-a.id;
});
newList=myList;
http://jsbin.com/kenobunali/edit?console
You could splice the wanted part and use splice to insert it at the end of the array.
var myList = [{ id: 0, name: 'Joe' }, { id: 1, name: 'Sally' }, { id: 2, name: 'Chris' }, { id: 3, name: 'Tiffany' }, { id: 4, name: 'Kerry' }],
id = 2;
myList.splice(myList.length, 0, myList.splice(0, myList.findIndex(o => o.id === id)));
console.log(myList);
using es6 spread syntax
var myList = [{ id: 0, name: 'Joe' }, { id: 1, name: 'Sally' }, { id: 2, name: 'Chris' }, { id: 3, name: 'Tiffany' }, { id: 4, name: 'Kerry' }],
id = 2;
var index = myList.findIndex(o => o.id == id);
var arr = myList.splice(0, index);
var result = [...myList, ...arr];
console.log(result);
I would like to know what is the best way to update the member of the multilevel object collection in JavaScript
Here is the simplified version of my collection:
this.Steps = [
{ id: 1, text: "test", childSteps:
[
{ id: 2, text: "test"},
{ id: 3, text: "test"},
{ id: 4, text: "test", childSteps:
[
{ id: 10, text: "test"},
{ id: 11, text: "test"}
]}
},
{ id: 5, text: "test"},
{ id: 6, text: "test"},
{ id: 7, text: "test"},
{ id: 8, text: "test"},
{ id: 9, text: "test"}
]
}
];
The ideal would be to have a function to be called like:
updateObjectByID(11, 'string to be set');
This is easy to be done when we have only 1 level of objects. But when using recursion on multilevel collection its getting much harder.
I'm currently using a function that is parsing the whole collection and building string like :
this.Steps[0].childSteps[3].childSteps[1].text == "string to be set"
and then I do eval() on that string.
I'm sure there might be a much cleaner solution.
Using eval is making my class impossible to compress btw.
Any help would be greatly appreciated.
Thanks in advance
You can build a map of objects by id for direct access:
var map = {};
(function recurse(steps) {
for (var i=0; i<steps.length; i++) {
var step = steps[i];
map[ step.id ] = step;
if ("childSteps" in step)
recurse(step.childSteps);
}
})(this.Steps);
function updateObjectByID(id, string) {
map[id].text = string;
}
A comment on your code: You overcomplified a lot. When the condition if(obj.id == objId) is met, you have your obj reference already!!! Now there is absolutely no need to search for a path to it, build a string from that, and eval it. Just assign directly to its property!
function(steps, objId, value, whatParam, command) {
if (typeof whatParam == 'undefined')
whatParam = 'selected';
$.each(steps, function(index, obj){
if(obj.id == objId) {
// removed a lot of unecessary stuff
// not sure, looks like "value" was evaled sometimes as well
obj[whatParam] = value;
} // insert an "else" here if ids are unique
if (typeof obj.childSteps != 'undefined') {
this.setupObjectState(obj.childSteps, objId, value, whatParam, command);
}
});
}
Since you call updateObjectById() with an ID, the ID has to be unique. Why don´t you change your structure like this then:
this.Steps = [
{ id: 1, text: "test" },
{ id: 2, parent: 1, text: "test" },
{ id: 3, parent: 1, text: "test" },
{ id: 4, parent: 1, text: "test" },
{ id: 10, parent: 4, text: "test" },
{ id: 11, parent: 4, text: "test" },
{ id: 5, parent: 1, text: "test"},
{ id: 6, parent: 1, text: "test"},
{ id: 7, parent: 1, text: "test"},
{ id: 8, parent: 1, text: "test"},
{ id: 9, parent: 1, text: "test"}
]
This way you can easily update your entries like this:
function updateObjectByID(id, text) {
for(var i = 0; i < this.Steps.length; i++) {
if(this.Steps[i].id === id) {
this.Steps[i].text = text;
break;
}
}
}
With the extra attribute parent you can still easily get all the children of an element like this:
function getChildrenByID(id) {
var array = [];
for(var i = 0; i < this.Steps.length; i++) {
if(this.Steps[i].parent === id) {
array.push(this.Steps[i]);
}
}
return array;
}
You must recursively descend your tree structure searching for the object with the target "id" and replace its text. For example:
function updateObjectByID(obj, id, text) {
if (!obj) return;
if (obj.id === id) {
obj.text = text;
} else if ((typeof(obj)==='object') && (obj.constructor===Array)) {
for (var i=0; i<obj.length; i++) {
updateObjectByID(obj[i], id, text);
}
} else if (obj.childSteps) {
updateObjectByID(obj.childSteps, id, text);
}
}
updateObjectByID(this.Steps, 11, 'String to be set');
However, this solution can be optimized by tree pruning.
function updateObjectByID(id, text) {
(function setText(steps) {
for(var i = 0; i < steps.length; i++){
var step = steps[i];
if(step.id === id){
step.text = text;
}
if ("childSteps" in step){
setText(step.childSteps);
}
}
})(this.Steps);
}
Here is demo. http://jsfiddle.net/qDWX8/