I am trying to get all object to append in a div but my updated values are not coming. If I assign a value while assigning my object, Object.entries can get all values.
My objects:
var allObjects = {
ivSetObject : {
id: "ivSet",
url: "../assets/img/ivSet.png"
},
gloveObject : {
id: "glove",
url: "../assets/img/glove.png"
},
tourniqueObject: {
id: "tournique",
url: "../assets/img/tournique.png"
},
trashObject: {
id: "trash",
url: "../assets/img/trash.png"
},
glove2Object: {
id: "glove2",
url: "../assets/img/glove.png"
},
ivSet2Object: {
id: "ivSet2",
url: "../assets/img/ivSet.png"
}
};
var selectedObjects = {};
I am adding values from here:
outerDiv.onclick = function () {
Object.defineProperty(selectedObjects, key, {
value: value
});
createSelectedDivs();
};
I am trying to call values from here:
var selectedContainer = document.getElementById("selectedItems");
function createSelectedDivs() {
console.log(selectedObjects);
selectedContainer.innerHTML = "";
for (const [key, value] of Object.entries(selectedObjects)) {
console.log("aa");
const outerDiv = document.createElement("div");
outerDiv.className = "selectedItemCard";
const imgDiv = document.createElement("img");
imgDiv.src = value.url;
outerDiv.appendChild(imgDiv);
selectedContainer.appendChild(outerDiv);
}
}
I can see selectedObjects in console.log but Object.entries(selectedObjects) is not working.
Based on mdn docs
Normal property addition through assignment creates properties which show up during property enumeration (for...in, Object.keys(), etc.), [...]. This method allows these extra details to be changed from their defaults. By default, properties added using Object.defineProperty() are not writable, not enumerable, and not configurable.
Your code:
const obj = {}
Object.defineProperty(obj, 'key', {
value: 'value'
})
console.log(Object.entries(obj))
With enumarable: true:
const obj = {}
Object.defineProperty(obj, 'key', {
value: 'value',
enumerable: true
})
console.log(Object.entries(obj))
The issue here is that Object.entries() returns an array of an object's enumerable properties and values, but does not update after the object has been modified. This means that if you update an object, the entries returned by Object.entries() will not reflect the changes.
To fix this, you need to call Object.entries() again after updating the object to get the updated values. For example:
let obj = {
a: 1,
b: 2
};
let entries = Object.entries(obj);
obj.a = 3;
// To get the updated values, we need to call Object.entries again
entries = Object.entries(obj);
console.log(entries); // [['a', 3], ['b', 2]]
Related
I'm attempting to re-map an Object that comes from an API call.
The format of the response is the following:
data: {
foo: [{
id: "1",
name: "joe",
info: "whatever"
}, {
id: "2",
name: "anna",
info: "whatever"
},...],
bar: [...]
}
The code I'm using to re-map the object inside each response array is:
const DATA = response.data;
const entries = Object.entries(DATA);
for (const entry of entries) {
entry[1].map(entry => ({
id: entry["id"],
mixed_info: entry["name"] + ", " + entry["info"]
}));
}
When I console.log the data after this, it shows the same as the initial response, as if it completly ignored the map function.
What am I doing wrong? Thanks for the attention.
map returns a new array, you are ignoring the result of the call.
Assign the result of the map call:
entry[1] = entry[1].map(...);
Array.prototype.map returns a new array - it doesn't modify the original.
You have to reassign the entry:
entry[1] = entry[1].map(/*...*/);
However that will only get reflected to the array inside entries, it won't change DATA. To change that, you have to either turn the key value pairs back into an object at the end:
DATA = Object.fromEntries(entries);
Or you have to reassign the DATA properties while iterating:
DATA[ entry[0] ] = entry[1].map(/*...*/);
I'd do:
const { data } = response;
for(const [key, value] of Object.entries(data)) {
data[key] = value.map(/*...*/);
}
let result = {};
for (const entry of entries) {
result[entry[0]] = entry[1].map(entry => ({
id: entry.id,
mixed_info: `${entry["name"] }, ${ entry["info"]}`,
}));
}
'result' contains exact remapped object.
This question already has answers here:
How to set value to a property in a Javascript object, which is identified by an array of keys
(2 answers)
Closed 3 years ago.
I have an object that resembles this:
const obj = {
prop1: {
prop2: {
value: 'something',
...otherProps
}
},
...otherProps
}
And an array that looks like this:
const history = ['prop1', 'prop2', 'value']
How do I assign the property value of prop2 a new value in a way that would also work for any other depth.
Just loop through the property list and get each property of the object.
const obj = {
prop1: {
prop2: {
value: 'something'
}
}
};
const history = ['prop1', 'prop2', 'value'];
console.log(setPropertyValue(obj, history, "test"));
console.log(getPropertyValue(obj, history));
function getPropertyValueContainer(values, propertyList) {
var copy = propertyList.slice(), propertyName = copy.pop();
for (var property of copy) values = values[property];
return { propertyName: propertyName, values: values };
}
function getPropertyValue(values, propertyList) {
var container = getPropertyValueContainer(values, propertyList);
return container.values[container.propertyName];
}
function setPropertyValue(values, propertyList, value) {
var container = getPropertyValueContainer(values, propertyList);
return container.values[container.propertyName] = value;
}
You can use references.
So here i am taking reference of object in a variable untill i key equal to value and than adding value using that ref.
const obj = { prop1: { prop2: { value: 'something'}}}
const history = ['prop1', 'prop2', 'value']
let ref = obj;
history.forEach((e,index)=>{
if(e !== 'value' ) ref = ref[e]
})
ref.value = 'xyz'
console.log(obj)
I have an api data
current: {
errors: {},
items: {
Economy: {}
}
}
Object key "Economy" can be different, for instance "Advance"
and i call it like
let current = current.items.Economy
or let current = current.items.Advance
How can i call it dynamically?
P.S. My front don't know what key will be return
Use Object.entries to get every key and value pair in an object. If items only has one such key-value pair, then just select the first entry:
const obj = {
current: {
errors: {},
items: {
Foo: {data: 'foo data'}
}
}
};
const [key, val] = Object.entries(obj.current.items)[0];
console.log(key);
console.log(val);
If you don't actually care about the dynamic key name and just want the inner object, use Object.values instead.
You can use Object.keys(current.items).
let current = {
errors: {},
items: {
Economy: {age: 10}
}
}
let keys = Object.keys(current.items);
let currentVal = current.items[keys[0]];
console.log(currentVal);
You can also use for loop:
let current = {
errors: {},
items: {
Economy: {age: 10}
}
}
for(var key in current.items){
console.log(current.items[key]);
}
I have an array of objects that can be of any length and any depth. I need to be able to find an object by its id and then modify that object within the array. Is there an efficient way to do this with either lodash or pure js?
I thought I could create an array of indexes that led to the object but constructing the expression to access the object with these indexes seems overly complex / unnecessary
edit1; thanks for all yours replies I will try and be more specific. i am currently finding the location of the object I am trying to modify like so. parents is an array of ids for each parent the target object has. ancestors might be a better name for this array. costCenters is the array of objects that contains the object I want to modify. this function recurses and returns an array of indexes that lead to the object I want to modify
var findAncestorsIdxs = function(parents, costCenters, startingIdx, parentsIdxs) {
var idx = startingIdx ? startingIdx : 0;
var pidx = parentsIdxs ? parentsIdxs : [];
_.each(costCenters, function(cc, ccIdx) {
if(cc.id === parents[idx]) {
console.log(pidx);
idx = idx + 1;
pidx.push(ccIdx);
console.log(pidx);
pidx = findAncestorsIdx(parents, costCenters[ccIdx].children, idx, pidx);
}
});
return pidx;
};
Now with this array of indexes how do I target and modify the exact object I want? I have tried this where ancestors is the array of indexes, costCenters is the array with the object to be modified and parent is the new value to be assigned to the target object
var setParentThroughAncestors = function(ancestors, costCenters, parent) {
var ccs = costCenters;
var depth = ancestors.length;
var ancestor = costCenters[ancestors[0]];
for(i = 1; i < depth; i++) {
ancestor = ancestor.children[ancestors[i]];
}
ancestor = parent;
console.log(ccs);
return ccs;
};
this is obviously just returning the unmodified costCenters array so the only other way I can see to target that object is to construct the expression like myObjects[idx1].children[2].grandchildren[3].ggranchildren[4].something = newValue. is that the only way? if so what is the best way to do that?
You can use JSON.stringify for this. It provides a callback for each visited key/value pair (at any depth), with the ability to skip or replace.
The function below returns a function which searches for objects with the specified ID and invokes the specified transform callback on them:
function scan(id, transform) {
return function(obj) {
return JSON.parse(JSON.stringify(obj, function(key, value) {
if (typeof value === 'object' && value !== null && value.id === id) {
return transform(value);
} else {
return value;
}
}));
}
If as the problem is stated, you have an array of objects, and a parallel array of ids in each object whose containing objects are to be modified, and an array of transformation functions, then it's just a matter of wrapping the above as
for (i = 0; i < objects.length; i++) {
scan(ids[i], transforms[i])(objects[i]);
}
Due to restrictions on JSON.stringify, this approach will fail if there are circular references in the object, and omit functions, regexps, and symbol-keyed properties if you care.
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_native_JSON#The_replacer_parameter for more info.
As Felix Kling said, you can iterate recursively over all objects.
// Overly-complex array
var myArray = {
keyOne: {},
keyTwo: {
myId: {a: '3'}
}
};
var searchId = 'myId', // Your search key
foundValue, // Populated with the searched object
found = false; // Internal flag for iterate()
// Recursive function searching through array
function iterate(haystack) {
if (typeof haystack !== 'object' || haystack === null) return; // type-safety
if (typeof haystack[searchId] !== 'undefined') {
found = true;
foundValue = haystack[searchId];
return;
} else {
for (var i in haystack) {
// avoid circular reference infinite loop & skip inherited properties
if (haystack===haystack[i] || !haystack.hasOwnProperty(i)) continue;
iterate(haystack[i]);
if (found === true) return;
}
}
}
// USAGE / RESULT
iterate(myArray);
console.log(foundValue); // {a: '3'}
foundValue.b = 4; // Updating foundValue also updates myArray
console.log(myArray.keyTwo.myId); // {a: '3', b: 4}
All JS object assignations are passed as reference in JS. See this for a complete tutorial on objects :)
Edit: Thanks #torazaburo for suggestions for a better code.
If each object has property with the same name that stores other nested objects, you can use: https://github.com/dominik791/obj-traverse
findAndModifyFirst() method should solve your problem. The first parameter is a root object, not array, so you should create it at first:
var rootObj = {
name: 'rootObject',
children: [
{
'name': 'child1',
children: [ ... ]
},
{
'name': 'child2',
children: [ ... ]
}
]
};
Then use findAndModifyFirst() method:
findAndModifyFirst(rootObj, 'children', { id: 1 }, replacementObject)
replacementObject is whatever object that should replace the object that has id equal to 1.
You can try it using demo app:
https://dominik791.github.io/obj-traverse-demo/
Here's an example that extensively uses lodash. It enables you to transform a deeply nested value based on its key or its value.
const _ = require("lodash")
const flattenKeys = (obj, path = []) => (!_.isObject(obj) ? { [path.join('.')]: obj } : _.reduce(obj, (cum, next, key) => _.merge(cum, flattenKeys(next, [...path, key])), {}));
const registrations = [{
key: "123",
responses:
{
category: 'first',
},
}]
function jsonTransform (json, conditionFn, modifyFn) {
// transform { responses: { category: 'first' } } to { 'responses.category': 'first' }
const flattenedKeys = Object.keys(flattenKeys(json));
// Easily iterate over the flat json
for(let i = 0; i < flattenedKeys.length; i++) {
const key = flattenedKeys[i];
const value = _.get(json, key)
// Did the condition match the one we passed?
if(conditionFn(key, value)) {
// Replace the value to the new one
_.set(json, key, modifyFn(key, value))
}
}
return json
}
// Let's transform all 'first' values to 'FIRST'
const modifiedCategory = jsonTransform(registrations, (key, value) => value === "first", (key, value) => value = value.toUpperCase())
console.log('modifiedCategory --', modifiedCategory)
// Outputs: modifiedCategory -- [ { key: '123', responses: { category: 'FIRST' } } ]
I needed to modify deeply nested objects too, and found no acceptable tool for that purpose. Then I've made this and pushed it to npm.
https://www.npmjs.com/package/find-and
This small [TypeScript-friendly] lib can help with modifying nested objects in a lodash manner. E.g.,
var findAnd = require("find-and");
const data = {
name: 'One',
description: 'Description',
children: [
{
id: 1,
name: 'Two',
},
{
id: 2,
name: 'Three',
},
],
};
findAnd.changeProps(data, { id: 2 }, { name: 'Foo' });
outputs
{
name: 'One',
description: 'Description',
children: [
{
id: 1,
name: 'Two',
},
{
id: 2,
name: 'Foo',
},
],
}
https://runkit.com/embed/bn2hpyfex60e
Hope this could help someone else.
I wrote this code recently to do exactly this, as my backend is rails and wants keys like:
first_name
and my front end is react, so keys are like:
firstName
And these keys are almost always deeply nested:
user: {
firstName: "Bob",
lastName: "Smith",
email: "bob#email.com"
}
Becomes:
user: {
first_name: "Bob",
last_name: "Smith",
email: "bob#email.com"
}
Here is the code
function snakeCase(camelCase) {
return camelCase.replace(/([A-Z])/g, "_$1").toLowerCase()
}
export function snakeCasedObj(obj) {
return Object.keys(obj).reduce(
(acc, key) => ({
...acc,
[snakeCase(key)]: typeof obj[key] === "object" ? snakeCasedObj(obj[key]) : obj[key],
}), {},
);
}
Feel free to change the transform to whatever makes sense for you!
Iam trying to push in array an object, but I get always error.
fCElements = [],
obj = {};
obj.fun = myFunction;
obj.id = 2;
fCElements.push ({
obj,
myid:2,
name:'klaus'
})
how I can push into array functions like "myFunction"?
Thanks
In the Object literal, you can only give key-value pairs. Your obj doesn't have any value.
Instead, you can do like this
var fCElements = [];
fCElements.push({
obj: {
fun: myFunction,
id: 2
},
myid: 2,
name: 'klaus'
});
Now, you are creating a new object, obj, on the fly, while pushing to the array. Now, your fCElements look like this
[ { obj: { fun: [Function], id: 2 }, myid: 2, name: 'klaus' } ]
You need to give your obj property a name (or a value).
var obj = {};
obj.fun = myFunction;
obj.id = 2;
fCElements.push ({
obj:obj,
myid:2,
name:'klaus'
});
The object you are pushing to the array seems off. It will try to push this object:
{
{fun: myfunction, id: 2},
myid: 2,
name: 'klaus'
}
Which is an invalid object since the first value has no key. You should do it like this instead:
fCElements.push ({
myObj:obj,
myid:2,
name:'klaus'
});