This question already has answers here:
Accessing an object property with a dynamically-computed name
(19 answers)
Closed 3 years ago.
I have an object like
let arr = [
{
title: "hello",
pivot: {
id: 1,
bid: 3
}
},
{
title: "home",
pivot: {
id: 2,
bid: 3
}
},
{
title: "nice",
pivot: {
id: 3,
bid: 3
}
}
];
I want to access its property dynamically. I want to access id property's value from pivot from first object of this array. And it's should be dynamic.
This is what I tried already.
let s = "0.pivot.id"
let res = arr[s]
console.log(res)
I can access by arr[0].pivot.id but this is not my case. I want it dynamically.
You can split the string and loop through it updating a variable refrencing the last found value :
let arr = [
{
title: "hello",
pivot: {
id: 1,
bid: 3
}
},
{
title: "home",
pivot: {
id: 2,
bid: 3
}
},
{
title: "nice",
pivot: {
id: 3,
bid: 3
}
}
];
let s = "0.pivot.id";
const getValue = (arr, str) => {
let ref = arr;
const keys = str.split(".");
keys.forEach(k => {
ref = ref[k];
});
return ref;
};
const result = getValue(arr, s);
console.log(result);
what you tried would give you a property which key was 0.pivot.id. So it might work if your object looks like this
{
'0.pivot.id': 'something'
}
There is no native way to access deeper level of an object dynamically. You would need to use recursion for that.
It's quite easy though, You could simply split your key into an array of keys and then recursively check your array for matching keys.
let arr = [
{
title: "hello",
pivot: {
id: 1,
bid: 3
}
},
{
title: "home",
pivot: {
id: 2,
bid: 3
}
},
{
title: "nice",
pivot: {
id: 3,
bid: 3
}
}
];
function getDynamicKeyRecursively(object, key) {
// the key needs to be an array,
// if it isn't, we split it into an array
if(typeof key === 'string') {
key = key.split('.');
}
// we get the current value of the current object
let currentValue = object[key[0]];
// remove the first index of the key
key.shift()
// if the current value is an object or an array, we recursively check this value for what we want
// otherwise, we return the value.
return Array.isArray(currentValue) || typeof currentValue === 'object' ? getDynamicKeyRecursively(currentValue, key) : currentValue;
}
console.log(getDynamicKeyRecursively(arr, '1.pivot.id'));
Related
that is suppose to come 1,2,3 but coming 3,3,3, how to fix that ?
Javascript updating automatically
let test = [ { id: 1 } ];
let test2 = [
{ id: 1 },
{ id: 2 },
{ id: 3 }
];
let x = []
test2.forEach(i => {
test[0].id = i.id;
x.push(test[0])
});
console.log(x)
Since you are pushing the same object 3 times and at the end of the loop it will have 3 reference of the same object i.e test[0]
You can use spread syntax to copy all properties of object
let test = [{ id: 1 }];
let test2 = [{ id: 1 }, { id: 2 }, { id: 3 }];
let x = [];
test2.forEach((i) => {
test[0].id = i.id;
x.push({ ...test[0] });
});
console.log(x);
Use the spread operator:
x.push({ ...test[0] })
Basically you need to shallow clone the array because it's an object; forEach will create 3 references to the same test[0] object at the beginning of the call.
You are passing in the same reference to the array everytime. You are updating that same value too i.e., test[0].
So in the end, you have an array with three elements, all 3 pointing to the same object whose id property you have updated to the final value - test2[2].id.
You can directly push in an object with the correct id property. You will not need an extra test array as you are creating your object and pushing them on the go.
let test = [ { id: 1 } ];
let test2 = [
{ id: 1 },
{ id: 2 },
{ id: 3 }
];
let x = []
test2.forEach(i => {
x.push({ id : i.id })
});
console.log(x)
This question already has answers here:
Merge property from an array of objects into another based on property value lodash
(5 answers)
Closed 4 years ago.
I have 2 array of objects
The first one called data:
const data = [
{
id: 1,
nombre: 'Piero',
},
{
id: 4,
nombre: 'Nelson',
},
{
id: 7,
nombre: 'Diego'
},
]
and the second called subs:
const subs = [
{
id: 1,
name: 'Temprano',
},
{
id: 4,
name: 'A tiempo',
},
{
id: 7,
name: 'Tarde'
},
]
In which I want to compare that if they have the same ID, the subs array will pass its name value to it and if it does not match that it puts a '-' in the data array, try this way:
data.forEach((d)=>{
subs.forEach((s)=>{
if(d.id === s.id){
d.subname = s.name;
}
else {
d.subname = '-';
}
});
});
But always assign the values with '-' as if it does not match any. What part am I doing wrong? Is there any other simpler way to do this? I would greatly appreciate your help.
The size of the subs array may vary.
It looks like you are not exiting the inner loop when a successful match is found.
In the first example where you are looking for a match for Piero, in your first iteration 1===1 and d.subname is correctly set to 'Temprano'. However, you then continue to compare the values- 1 !== 4 so Temprano is overwritten with '-', and 1 !== 7 so it is overwritten again.
An alternate approach:
data.forEach(d => {
const match = subs.find(s => s.id === d.id);
d.subname = match ? match.name : '-';});
I'd also recommend adding a case where you're not expecting to find a match, so you can see that it works in both cases!
https://codepen.io/anon/pen/MGGBLP?editors=0010
const data = [
{
id: 1,
nombre: 'Piero',
},
{
id: 4,
nombre: 'Nelson',
},
{
id: 7,
nombre: 'Diego'
},
];
const subs = [
{
id: 1,
name: 'Temprano',
},
{
id: 4,
name: 'A tiempo',
},
{
id: 7,
name: 'Tarde'
},
];
// by caching one of the arrays in an object, it reduces the run time to linear.
const obj = subs.reduce((acc, item) => {
acc[item.id] = item;
return acc;
})
data.forEach(d => {
if (d.id in obj) {
d.subname = obj[d.id].name;
} else {
d.subname = '-';
}
});
console.log(data);
You just need two lines for this:
var findIds = id => subs.find(findId => findId.id === id);
data.forEach(findId => Object.assign(findId, findIds(findId.id)));
Your data array object should now include the name property from it's respective id sharing object in subs array.
jsFiddle: https://jsfiddle.net/AndrewL64/9k1d3oj2/1/
dontMutateMeArray=[1,2,3,3,3,4,5];
toBeRemoved=3;
newArray=dontMutateMeArray.something(toBeRemoved); // [1,2,3,3,4,5]
iDontWantArray=dontMutateMeArray.filter(value=>value===toBeRemoved); // [1,2,4,5]
I indeed need it for array of objects too. And I specifically need to remove the last added object (ie. the one with higher index in the array). Something like:
dontMutateMeArray=[{id:1},{id:2},{id:3,sth:1},{id:3,sth:42},{id:3,sth:5},{id:4},{id:5}];
toBeRemoved=3;
newArray=dontMutateMeArray.something(toBeRemoved); // [{id:1},{id:2},{id:3,sth:1},{id:3,sth:42},{id:4},{id:5}]
iDontWantArray=dontMutateMeArray.filter(obj=>obj.id===toBeRemoved); // [{id:1},{id:2},{id:4},{id:5}]
iDontWantArray2=dontMutateMeArray.blahBlah(toBeRemoved); // [{id:1},{id:2},{id:3,sth:1},{id:3,sth:5},{id:4},{id:5}]
You could iterate from right and check with a closure.
var dontMutateMeArray = [{ id: 1 }, { id: 2 }, { id: 3, sth: 1 }, { id: 3, sth: 42 }, { id: 3, sth: 5 }, { id: 4 }, { id: 5 }],
toBeRemoved = 3,
newArray = dontMutateMeArray.reduceRight((found => (r, a) => (!found && a.id === toBeRemoved ? found = true : r.unshift(a), r))(false), []);
console.log(newArray);
I want to add non-duplicate objects into a new array.
var array = [
{
id: 1,
label: 'one'
},
{
id: 1,
label: 'one'
},
{
id: 2,
label: 'two'
}
];
var uniqueProducts = array.filter(function(elem, i, array) {
return array.indexOf(elem) === i;
});
console.log('uniqueProducts', uniqueProducts);
// output: [object, object, object]
live code
I like the class based approach using es6. The example uses lodash's _.isEqual method to determine equality of objects.
var array = [{
id: 1,
label: 'one'
}, {
id: 1,
label: 'one'
}, {
id: 2,
label: 'two'
}];
class UniqueArray extends Array {
constructor(array) {
super();
array.forEach(a => {
if (! this.find(v => _.isEqual(v, a))) this.push(a);
});
}
}
var unique = new UniqueArray(array);
console.log(unique);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.16.4/lodash.min.js"></script>
Usually, you use an object to keep track of your unique keys. Then, you convert the object to an array of all property values.
It's best to include a unique id-like property that you can use as an identifier. If you don't have one, you need to generate it yourself using JSON.stringify or a custom method. Stringifying your object will have a downside: the order of the keys does not have to be consistent.
You could create an objectsAreEqual method with support for deep comparison, but this will slow your function down immensely.
In two steps:
var array=[{id:1,label:"one"},{id:1,label:"one"},{id:2,label:"two"}];
// Create a string representation of your object
function getHash(obj) {
return Object.keys(obj)
.sort() // Keys don't have to be sorted, do it manually here
.map(function(k) {
return k + "_" + obj[k]; // Prefix key name so {a: 1} != {b: 1}
})
.join("_"); // separate key-value-pairs by a _
}
function getHashBetterSolution(obj) {
return obj.id; // Include unique ID in object and use that
};
// When using `getHashBetterSolution`:
// { '1': { id: '1', label: 'one' }, '2': /*etc.*/ }
var uniquesObj = array.reduce(function(res, cur) {
res[getHash(cur)] = cur;
return res;
}, {});
// Convert back to array by looping over all keys
var uniquesArr = Object.keys(uniquesObj).map(function(k) {
return uniquesObj[k];
});
console.log(uniquesArr);
// To show the hashes
console.log(uniquesObj);
You can use Object.keys() and map() to create key for each object and filter to remove duplicates.
var array = [{
id: 1,
label: 'one'
}, {
id: 1,
label: 'one'
}, {
id: 2,
label: 'two'
}];
var result = array.filter(function(e) {
var key = Object.keys(e).map(k => e[k]).join('|');
if (!this[key]) {
this[key] = true;
return true;
}
}, {});
console.log(result)
You could use a hash table and store the found id.
var array = [{ id: 1, label: 'one' }, { id: 1, label: 'one' }, { id: 2, label: 'two' }],
uniqueProducts = array.filter(function(elem) {
return !this[elem.id] && (this[elem.id] = true);
}, Object.create(null));
console.log('uniqueProducts', uniqueProducts);
Check with all properties
var array = [{ id: 1, label: 'one' }, { id: 1, label: 'one' }, { id: 2, label: 'two' }],
keys = Object.keys(array[0]), // get the keys first in a fixed order
uniqueProducts = array.filter(function(a) {
var key = keys.map(function (k) { return a[k]; }).join('|');
return !this[key] && (this[key] = true);
}, Object.create(null));
console.log('uniqueProducts', uniqueProducts);
You can use reduce to extract out the unique array and the unique ids like this:
var array=[{id:1,label:"one"},{id:1,label:"one"},{id:2,label:"two"}];
var result = array.reduce(function(prev, curr) {
if(prev.ids.indexOf(curr.id) === -1) {
prev.array.push(curr);
prev.ids.push(curr.id);
}
return prev;
}, {array: [], ids: []});
console.log(result);
.as-console-wrapper{top:0;max-height:100%!important;}
If you don't know the keys, you can do this - create a unique key that would help you identify duplicates - so I did this:
concat the list of keys and values of the objects
Now sort them for the unique key like 1|id|label|one
This handles situations when the object properties are not in order:
var array=[{id:1,label:"one"},{id:1,label:"one"},{id:2,label:"two"}];
var result = array.reduce(function(prev, curr) {
var tracker = Object.keys(curr).concat(Object.keys(curr).map(key => curr[key])).sort().join('|');
if(!prev.tracker[tracker]) {
prev.array.push(curr);
prev.tracker[tracker] = true;
}
return prev;
}, {array: [], tracker: {}});
console.log(result);
.as-console-wrapper{top:0;max-height:100%!important;}
Given an array of objects like this:
objects = [
{ id: 'aaaa', description: 'foo' },
{ id: 'bbbb', description: 'bar' },
{ id: 'cccc', description: 'baz' }
];
And an array of strings like this:
order = [ 'bbbb', 'aaaa', 'cccc' ];
How would I sort the first array so that the id attribute matches the order of the second array?
Try this:
objects.sort(function(a, b){
return order.indexOf(a.id) - order.indexOf(b.id)
});
Assuming the variables are like you declared them in the question, this should return:
[
{ id: 'bbbb', description: 'bar' },
{ id: 'aaaa', description: 'foo' },
{ id: 'cccc', description: 'baz' }
];
(It actually modifies the objects variable)
You need a way to translate the string into the position in the array, i.e. an index-of function for an array.
There is one in newer browsers, but to be backwards compatible you need to add it if it's not there:
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function(str) {
var i;
for (i = 0; i < this.length; i++) if (this[i] == str) return i;
return -1;
}
}
Now you can sort the array by turning the string into an index:
objects.sort(function(x,y){ return order.indexOf(x.id) - order.indexOf(y.id); });
Demo: http://jsfiddle.net/Guffa/u3CQW/
Use a mapping object for (almost) constant access time:
/* Create a mapping object `orderIndex`:
{
"bbbb": 0,
"aaaa": 1,
"cccc": 2
}
*/
const orderIndex = {}
order.forEach((value, index) => orderIndex[value] = index);
// Sort
objects.sort((a, b) => orderIndex[a.id] - orderIndex[b.id]);
// data
const objects = [
{ id: 'aaaa', description: 'foo' },
{ id: 'bbbb', description: 'bar' },
{ id: 'cccc', description: 'baz' }
];
const order = [ 'bbbb', 'aaaa', 'cccc' ];
/* Create a mapping object `orderIndex`:
{
"bbbb": 0,
"aaaa": 1,
"cccc": 2
}
*/
const orderIndex = {}
order.forEach((value, index) => orderIndex[value] = index);
// Sort
objects.sort((a, b) => orderIndex[a.id] - orderIndex[b.id]);
// Log
console.log('orderIndex:', orderIndex);
console.log('objects:', objects);