Just a bit confused as to why the following is occurring:
let groupdetails = {
groupName: "",
};
const groupsArray = [];
groupdetails.groupName = 'A'
groupsArray.push(groupdetails)
groupdetails.groupName = 'B'
groupsArray.push(groupdetails)
console.log(groupsArray)
The result I am getting is:
[ { groupName: "B" }, { "groupName": "B" } ]
The result I was expecting was:
[ { groupName: "A" }, { "groupName": "B" } ]
Unsure what I am doing wrong?
You should create a different reference each time you push an element
let groupdetails = {
groupName: "",
};
const groupsArray = [];
groupdetails.groupName = 'A'
groupsArray.push(groupdetails)
groupdetails = {...groupdetails}
groupdetails.groupName = 'B'
groupsArray.push(groupdetails)
console.log(groupsArray)
You are facing issue with Shallow Copy and Deep Copy..
In your case eventhough you were updating the value groupdetails.groupName = 'B', this referes to the same node as the one you pushed initially. You have to creat a new node with spread operater or some thing and update its value and push it again.
In order to achieve your requirement you have to follow something like below mentioned.
let groupdetails = {
groupName: "",
};
const groupsArray = [];
groupdetails.groupName = 'A'
groupsArray.push(groupdetails);
const newNode = { ...groupdetails };
newNode.groupName = 'B';
groupsArray.push(newNode);
console.log(groupsArray);
Remember Arrays, Objects are copied by references, not just by values
a = 'John';
b = a;
console.log(a === b);
Here values are copied to another variable.
obj = {name: 'John'};
newObj = obj;
newObj.name = 'Kate';
console.log(newObj); // {name: 'Kate'}
console.log(obj) // {name: 'Kate'}
Here the reference is passed to the other object. So once an object is modified it will be reflected on the other object.
In your case, this will work,
const groupsArray = [];
function addNew(value) {
return { groupName: value}
}
groupsArray.push(addNew('A'));
groupsArray.push(addNew('B'));
console.log(groupsArray);
How do you set a multi-level deep update on an object with a dynamic key in javascript? When i try a typical dynamic update, it adds another key/value pair instead of updating the correct parameter.
let home = {
street: {
house: {
room: {
window: true
}
}
}
}
let update = {
key: "house.room.window",
value: "false"
}
home.street[update.key] = update.value;
console.log(home);
expected:
home = {
street:{
house: {
room: {
window: false
}
}
}
}
instead i get:
home = {
street:{
house: {
room: {
window: true
}
}
"house.room.window": false
}
}
Try like below. Explanation is in comments.
let home = {
street: {
house: {
room: {
window: true
}
}
}
}
let update = {
key: "house.room.window",
value: "false"
}
// select object to get updated
let obj = home.street;
// get array of nested keys
let nestedKeys = update.key.split('.');
// get object from nested keys until last key
// used slice(0, -1) so it will iterate through all key except last one
nestedKeys.slice(0, -1).forEach(k => obj = obj[k]);
// use object with last key and update value
obj[nestedKeys[nestedKeys.length - 1]] = update.value
// log object
console.log(home);
If you don't mind using a library, you can try lodash's update.
_.update(home, update.key, () => update.value);
arr = ["sadik", "arif", "rahman"]
I want to create a nested object with the same key but different value like:
{
subcategory: {
name: sadik
subcategory: {
name: arif
subcategory: {
name: rahman
}
}
}
}
my code:
let arr = ['sadik', 'arif', 'babor']
let obj = {}
arr.forEach((elem) => {
let a = {}
a["subcategory"] = {name:elem}
Object.assign(obj, a)
})
i get only last value:
{
subcategory: {
name:"babor"
}
}
Your code did not work because you were replacing your "subcategory" key value in each iteration. You have change the reference object to the next nested level each time to get the expected output, see the working snippet below:
const arr = ['level-1', 'level-2', 'level-3']
const obj = {}
let refObj = obj;
arr.forEach( ele => {
refObj = refObj['subcategory'] || refObj;
refObj['subcategory'] = { 'name': ele};
})
console.log('output', obj);
In Firestore you can update fields in nested objects by a dot notation (https://firebase.google.com/docs/firestore/manage-data/add-data?authuser=0#update_fields_in_nested_objects). I wonder how to make that work in Typescript / Javascript.
For example the following object:
const user = {
id: 1
details: {
name: 'Max',
street: 'Examplestreet 38',
email: {
address: 'max#example.com',
verified: true
}
},
token: {
custom: 'safghhattgaggsa',
public: 'fsavvsadgga'
}
}
How can I update this object with the following changes:
details.email.verified = false;
token.custom = 'kka';
I already found that Lodash has a set function:
_.set(user, 'details.email.verified', false);
Disadvantage: I have to do this for every change. Is their already a method to update the object with an object (like firestore did)?
const newUser = ANYFUNCTION(user, {
'details.email.verified': false,
'token.custom' = 'kka'
});
// OUTPUT for newUser would be
{
id: 1
details: {
name: 'Max',
street: 'Examplestreet 38',
email: {
address: 'max#example.com',
verified: false
}
},
token: {
custom: 'kka',
public: 'fsavvsadgga'
}
}
Does anyone know an good solution for this? I already found more solutions if I only want to change one field (Dynamically set property of nested object), but no solution for more than one field with one method
I think you are stuck with using a function but you could write it yourself. No need for a lib:
function set(obj, path, value) {
let parts = path.split(".");
let last = parts.pop();
let lastObj = parts.reduce((acc, cur) => acc[cur], obj);
lastObj[last] = value;
}
set(user, 'details.email.verified', false);
if what you want to do is merge 2 objects then it is a bit trickier:
function forEach(target, fn) {
const keys = Object.keys(target);
let i = -1;
while (++i < keys.length) {
fn(target[keys[i]], keys[i]);
}
}
function setValues(obj, src) {
forEach(src, (value, key) => {
if (value !== null && typeof (value) === "object") {
setValues(obj[key], value);
} else {
obj[key] = value;
}
});
}
let obj1 = {foo: {bar: 1, boo: {zot: null}}};
let obj2 = {foo: {baz: 3, boo: {zot: 5}}};
setValues(obj1, obj2);
console.log(JSON.stringify(obj1));
One solution in combination with lodash _.set method could be:
function setObject(obj, paths) {
for (const p of Object.keys(paths)) {
obj = _.set(obj, p, paths[p]);
}
return obj;
}
I'm trying to setup an object literal in a JavaScript script that has a key with multiple names. referring to the same object value i.e. something like these that I have already tried:
var holidays: {
"thanksgiving day", "thanksgiving", "t-day": {
someValue : "foo"
}
}
var holidays: {
["thanksgiving day", "thanksgiving", "t-day"]: {
someValue : "foo"
}
}
Is there a way I can accomplish this?
Another approach is to do some postprocessing
function expand(obj) {
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; ++i) {
var key = keys[i],
subkeys = key.split(/,\s?/),
target = obj[key];
delete obj[key];
subkeys.forEach(function(key) { obj[key] = target; })
}
return obj;
}
var holidays = expand({
"thanksgiving day, thanksgiving, t-day": {
someValue : "foo"
}
});
JSON does not offer such a feature, nor do Javascript object literals.
You might be able to make do with something like this:
holidays = {
thanksgiving: {foo: 'foo'},
groundhogDay: {foo: 'bar'},
aliases: {
'thanksgiving day': 'thanksgiving',
't-day': 'thanksgiving',
'Bill Murrays nightmare': 'groundhogDay'
}
}
and then you can check
holidays[name] || holidays[holidays.aliases[name]]
for your data.
It's not a wonderful solution. But it wouldn't be too difficult to write a little function that created this sort of object out of a representation like:
[
{
names: ['thanksgiving', 'thanksgiving day', 't-day'],
obj: {foo: 'foo'}
},
{
names: ['groundhogDay', 'Bill Murrays nightmare'],
obj: {foo: 'bar'}
},
]
if that would be easier to maintain.
Another solution, if you can afford RegExp execution, and ES6 Proxy:
let align = new Proxy({
'start|top|left': -1,
'middle|center': 0,
'end|bottom|right': 1,
}, {
get: function(target, property, receiver) {
for (let k in target)
if (new RegExp(k).test(property))
return target[k]
return null
}
})
align.start // -1
align.top // -1
align.left // -1
align.middle // 0
align.center // 0
align.end // 1
align.bottom // 1
align.right // 1
See MDN Proxy
2021 EDIT:
Another (cleaner?) solution using reduce & defineProperty :
const myDict = [
// list of pairs [value, keys],
// note that a key should appear only once
[-1, ['start', 'left', 'top']],
[0, ['center', 'middle']],
[1, ['end', 'right', 'bottom']],
].reduce((obj, [value, keys]) => {
for (const key of keys) {
Object.defineProperty(obj, key, { value })
}
return obj
}, {})
I guess you could do something like this:
var holidays = {
'thanksgiving day': {
foo: 'foo'
}
};
holidays.thanksgiving = holidays['t-day'] = holidays['thanksgiving day'];
If you see yourself doing this often or you have more values consider this pattern:
'thanksgiving, t-day, thanks, thank, thank u'.split(',').forEach(function(key) {
holidays[key] = holidays['thanksgiving day'];
});
A better approach would be to process your data beforehand instead of adding duplicates.
That should work as expected:
function getItem(_key) {
items = [{
item: 'a',
keys: ['xyz','foo']
},{
item: 'b',
keys: ['xwt','bar']
}];
_filtered = items.filter(function(item) {
return item.keys.indexOf(_key) != -1
}).map(function(item) {
return item.item;
});
return !!_filtered.length ? _filtered[0] : false;
}
With ES6 you could do it like this, but it's not ideal:
const holidays = {
"single": {
singleValue: "foo",
},
...([
"thanksgiving day", "thanksgiving", "t-day",
].reduce((a, v) => ({...a, [v]: {
someValue: "foo",
}}), {})),
"other": {
otherValue: "foo",
},
};
I still think the cleanest solution is probably:
let holidays = {
"t-day": {
someValue: "foo",
},
};
holidays["thanksgiving"] = holidays["t-day"];
holidays["thanksgiving day"] = holidays["t-day"];
Now this may be overkill for you, but here's a generic function that will create an object with "multiple keys." What it actually does is have one real property with the actual value, and then defines getters and setters to forward operations from the virtual keys to the actual property.
function multiKey(keyGroups) {
let obj = {};
let props = {};
for (let keyGroup of keyGroups) {
let masterKey = keyGroup[0];
let prop = {
configurable: true,
enumerable: false,
get() {
return obj[masterKey];
},
set(value) {
obj[masterKey] = value;
}
};
obj[masterKey] = undefined;
for (let i = 1; i < keyGroup.length; ++i) {
if (keyGroup.hasOwnProperty(i)) {
props[keyGroup[i]] = prop;
}
}
}
return Object.defineProperties(obj, props);
}
This is less sketchy than you would expect, has basically no performance penalty once the object is created, and behaves nicely with enumeration (for...in loops) and membership testing (in operator). Here's some example usage:
let test = multiKey([
['north', 'up'],
['south', 'down'],
['east', 'left'],
['west', 'right']
]);
test.north = 42;
test.down = 123;
test.up; // returns 42
test.south; // returns 123
let count = 0;
for (let key in test) {
count += 1;
}
count === 4; // true; only unique (un-linked) properties are looped over
Taken from my Gist, which you may fork.
Same reponse (ES6 Proxy, RegExp), but in a shorter way (and significantly less legible)
let align = new Proxy({
'start|top|left': -1,
'middle|center': 0,
'end|bottom|right': 1,
}, { get: (t, p) => Object.keys(t).reduce((r, v) => r !== undefined ? r : (new RegExp(v).test(p) ? t[v] : undefined), undefined) })
align.start // -1
align.top // -1
align.left // -1
align.middle // 0
align.center // 0
align.end // 1
align.bottom // 1
align.right // 1
//create some objects(!) you want to have aliases for..like tags
var {learn,image,programming} =
["learn", "image", "programming"].map(tag=>({toString:()=>tag }));
//create arbitrary many aliases using a Map
var alias = new Map();
alias.set("photo", image);
alias.set("pic", image);
alias.set("learning", learn);
alias.set("coding", programming);
//best put the original tagNames in here too..
//pretty easy huh?
// returns the image object
alias.get("pic");
// ;)
here is a way you can initialize an object with several keys sharing the same value
var holidays = {
...["thanksgiving day", "thanksgiving", "t-day"].reduce((acc, key) => ({ ...acc, [key]: 'foo' }), {})
}
although I would personally think it was more clear if it was written out
Object.fromEntries produces some fairly readable and concise code:
var holidays = Object.fromEntries(
["thanksgiving day", "thanksgiving", "t-day"].map(k => [k, "foo"]));
The spread syntax can be used to include this alongside other key/value pairs:
var holidaysAndMore = {
"A": "a",
...Object.fromEntries(
["thanksgiving day", "thanksgiving", "t-day"].map(k => [k, "foo"])),
"B": "b"
};