Access nested property by path - javascript

I'm trying to access to nested properties of an object from a string.
Here is my sample code :
var obj = {
'text': 'hello',
'foo': {
'float': 0.5,
'bar': {
'id': 42
}
}
};
var keyOne = 'text';
var keyTwo = 'foo.float';
var keyThree = 'foo.bar.id';
console.log(obj[keyOne]); // successfully log 'hello'
console.log(obj[keyTwo]); // trying to log '0.5'
console.log(obj[keyThree]); // trying to log '42'
I'm trying to do it in JS but I also have jQuery ready for a cleaner solution.

You'll have to do a little bit of traversal for that.
Split the path by it's ., then Array.reduce over the parts with each iteration accessing the property it refers to via a bracket-notation accessor.
Eventually you'll reach the value you're after.
var obj = {
'text': 'hello',
'foo': {
'float': 0.5,
'bar': {
'id': 42,
'baz': [{ name: 'Mary' }, { name: 'Jane' }]
}
}
};
var getValueByPath = (obj, path) =>
path.split('.').reduce((acc, part) => acc ? acc[part] : undefined, obj);
var keyOne = 'text';
var keyTwo = 'foo.float';
var keyThree = 'foo.bar.id';
var keyFour = 'foo.bar.baz.1.name';
console.log(getValueByPath(obj, keyOne));
console.log(getValueByPath(obj, keyTwo));
console.log(getValueByPath(obj, keyThree));
console.log(getValueByPath(obj, keyFour));

Whats wrong with accessing like this? is there a reason you need the keys to be defined in variables?
Hope this helps :)
var obj = {
text: 'hello',
foo: {
float: 0.5,
bar: {
id: 42,
},
},
};
console.log(obj.text);
console.log(obj.foo.float);
console.log(obj.foo.bar.id);

Related

Create object from multiple object with same value in map JS [duplicate]

I have an array of objects:
[
{ key : '11', value : '1100', $$hashKey : '00X' },
{ key : '22', value : '2200', $$hashKey : '018' }
];
How do I convert it into the following by JavaScript?
{
"11": "1100",
"22": "2200"
}
Tiny ES6 solution can look like:
var arr = [{key:"11", value:"1100"},{key:"22", value:"2200"}];
var object = arr.reduce(
(obj, item) => Object.assign(obj, { [item.key]: item.value }), {});
console.log(object)
Also, if you use object spread, than it can look like:
var object = arr.reduce((obj, item) => ({...obj, [item.key]: item.value}) ,{});
One more solution that is 99% faster is(tested on jsperf):
var object = arr.reduce((obj, item) => (obj[item.key] = item.value, obj) ,{});
Here we benefit from comma operator, it evaluates all expression before comma and returns a last one(after last comma). So we don't copy obj each time, rather assigning new property to it.
This should do it:
var array = [
{ key: 'k1', value: 'v1' },
{ key: 'k2', value: 'v2' },
{ key: 'k3', value: 'v3' }
];
var mapped = array.map(item => ({ [item.key]: item.value }) );
var newObj = Object.assign({}, ...mapped );
console.log(newObj );
One-liner:
var newObj = Object.assign({}, ...(array.map(item => ({ [item.key]: item.value }) )));
You're probably looking for something like this:
// original
var arr = [
{key : '11', value : '1100', $$hashKey : '00X' },
{key : '22', value : '2200', $$hashKey : '018' }
];
//convert
var result = {};
for (var i = 0; i < arr.length; i++) {
result[arr[i].key] = arr[i].value;
}
console.log(result);
I like the functional approach to achieve this task:
var arr = [{ key:"11", value:"1100" }, { key:"22", value:"2200" }];
var result = arr.reduce(function(obj,item){
obj[item.key] = item.value;
return obj;
}, {});
Note: Last {} is the initial obj value for reduce function, if you won't provide the initial value the first arr element will be used (which is probably undesirable).
https://jsfiddle.net/GreQ/2xa078da/
Using Object.fromEntries:
const array = [
{ key: "key1", value: "value1" },
{ key: "key2", value: "value2" },
];
const obj = Object.fromEntries(array.map(item => [item.key, item.value]));
console.log(obj);
A clean way to do this using modern JavaScript is as follows:
const array = [
{ name: "something", value: "something" },
{ name: "somethingElse", value: "something else" },
];
const newObject = Object.assign({}, ...array.map(item => ({ [item.name]: item.value })));
// >> { something: "something", somethingElse: "something else" }
you can merge array of objects in to one object in one line:
const obj = Object.assign({}, ...array);
Use lodash!
const obj = _.keyBy(arrayOfObjects, 'keyName')
Update: The world kept turning. Use a functional approach instead.
Previous answer
Here you go:
var arr = [{ key: "11", value: "1100" }, { key: "22", value: "2200" }];
var result = {};
for (var i=0, len=arr.length; i < len; i++) {
result[arr[i].key] = arr[i].value;
}
console.log(result); // {11: "1000", 22: "2200"}
Simple way using reduce
// Input :
const data = [{key: 'value'}, {otherKey: 'otherValue'}];
data.reduce((prev, curr) => ({...prev, ...curr}) , {});
// Output
{key: 'value', otherKey: 'otherValue'}
More simple Using Object.assign
Object.assign({}, ...array);
Using Underscore.js:
var myArray = [
Object { key="11", value="1100", $$hashKey="00X"},
Object { key="22", value="2200", $$hashKey="018"}
];
var myObj = _.object(_.pluck(myArray, 'key'), _.pluck(myArray, 'value'));
Nearby 2022, I like this approach specially when the array of objects are dynamic which also suggested based on #AdarshMadrecha's test case scenario,
const array = [
{ key : '11', value : '1100', $$hashKey : '00X' },
{ key : '22', value : '2200', $$hashKey : '018' }];
let obj = {};
array.forEach( v => { obj[v.key] = v.value }) //assign to new object
console.log(obj) //{11: '1100', 22: '2200'}
let array = [
{ key: "key1", value: "value1" },
{ key: "key2", value: "value2" },
];
let arr = {};
arr = array.map((event) => ({ ...arr, [event.key]: event.value }));
console.log(arr);
Was did yesterday
// Convert the task data or array to the object for use in the above form
const {clientData} = taskData.reduce((obj, item) => {
// Use the clientData (You can set your own key name) as the key and the
// entire item as the value
obj['clientData'] = item
return obj
}, {});
Here's how to dynamically accept the above as a string and interpolate it into an object:
var stringObject = '[Object { key="11", value="1100", $$hashKey="00X"}, Object { key="22", value="2200", $$hashKey="018"}]';
function interpolateStringObject(stringObject) {
var jsObj = {};
var processedObj = stringObject.split("[Object { ");
processedObj = processedObj[1].split("},");
$.each(processedObj, function (i, v) {
jsObj[v.split("key=")[1].split(",")[0]] = v.split("value=")[1].split(",")[0].replace(/\"/g,'');
});
return jsObj
}
var t = interpolateStringObject(stringObject); //t is the object you want
http://jsfiddle.net/3QKmX/1/
// original
var arr = [{
key: '11',
value: '1100',
$$hashKey: '00X'
},
{
key: '22',
value: '2200',
$$hashKey: '018'
}
];
// My solution
var obj = {};
for (let i = 0; i < arr.length; i++) {
obj[arr[i].key] = arr[i].value;
}
console.log(obj)
You can use the mapKeys lodash function for that. Just one line of code!
Please refer to this complete code sample (copy paste this into repl.it or similar):
import _ from 'lodash';
// or commonjs:
// const _ = require('lodash');
let a = [{ id: 23, title: 'meat' }, { id: 45, title: 'fish' }, { id: 71, title: 'fruit' }]
let b = _.mapKeys(a, 'id');
console.log(b);
// b:
// { '23': { id: 23, title: 'meat' },
// '45': { id: 45, title: 'fish' },
// '71': { id: 71, title: 'fruit' } }

Perform .join on a complex object with array of object dynamically

I have a complex js object, that contains arrays of an object. The problem is some of the main object properties' arrays can have a different property.
var foo = {};
foo.prop1 = [
{name:"test", skill:1},
{name:"test2", skill:2},
];
foo.prop2 = [
{address:"Earth",distance:1},
{address:"Mars", distance:2}
]
My aim is to just replace the main object property value with the joined values for retrieval.
This is what I have right now.
if(Object.keys(foo).length){
Object.keys(foo).forEach(key => {
var x = foo[key];
if(key === "address") {
foo[key] = x.map(function(elem){return elem.address;}).join(";");
} else {
foo[key] = x.map(function(elem){return elem.name;}).join(";");
}
});
}
How can I make it dynamic so that I don't need to use the if statement? I just want to join all the first property of the inner obj.
Result:
foo new values would be:
foo.prop1 = test;test2
foo.prop2 = Earth;Mars
I got it. I just want to join the first property of the sub object.
I replaced the if with this
foo[key] = x.map(function(elem){return elem[Object.keys(elem)[0]]; }).join(";");
I guess you are trying to choose the value with string type
var foo = {};
foo.prop1 = [{
name: "test",
skill: 1
},
{
name: "test2",
skill: 2
},
];
foo.prop2 = [{
address: "Earth",
distance: 1
},
{
address: "Mars",
distance: 2
}
]
function formulate() {
const result = {};
(Object.keys(foo) || []).forEach(function(k) {
result[k] = foo[k].map(function(val) {
str_key = Object.keys(val).filter(function(val_k) {
return typeof val[val_k] === "string";
});
return str_key.map(function(s) {
return val[s];
});
}).join(";");
});
return result;
}
result = formulate()
console.log(result);
I hope, this will work for you
var foo = {};
foo.prop1 = [
{name:"test", skill:1},
{name:"test2", skill:2},
];
foo.prop2 = [
{address:"Earth",distance:1},
{address:"Mars", distance:2}
]
Object.keys(foo).forEach(key => {
foo[key]=foo[key].map(val => { return Object.entries(val)[0][1] } ).toString().split(",").join(";")
});
console.log(foo)

Setting a deep JS object key using a lookup string

How do I set deep properties in a JavaScript object using a dot-syntax string to specify which property I want to change?
For simple objects, I could just use data['property_name'] = 'foo', but I don't necessarily know how deeply nested the data is going to be.
Below is some example code with how I'd like to be able to format the data in the end. For all I know there's a nice way that JS already allows you to do this, but I haven't been able to find it yet.
Plunker here.
var items = [
{
lookup_string: "User.UserProfile.name",
value: "John Smith"
},
{
lookup_string: "User.email",
value: "johnsmith#example.com"
},
]
var data = {};
items.forEach(function(item){
// Inside this loop, set the appropriate keys under data. Is there a non-convoluted way to do this?
});
console.log("items", items);
console.log("Results", data)
// In the end, data should look like this:
var desiredData = {
User: {
UserProfile: {
name: 'John Smith'
},
email: 'johnsmith#example.com'
}
}
You could split the lookup_string and reduce an object with a default object. Later assign the value.
function setValue(object, path, value) {
var keys = path.split('.'),
last = keys.pop();
keys.reduce(function (o, k) {
return o[k] = o[k] || {};
}, object)[last] = value;
}
var items = [{ lookup_string: "User.UserProfile.name", value: "John Smith" }, { lookup_string: "User.email", value: "johnsmith#example.com" }],
object = {};
items.forEach(function(o) {
setValue(object, o.lookup_string, o.value);
});
console.log(object);
.as-console-wrapper { max-height: 100% !important; top: 0; }
you will have to loop over all the keys from lookup_string.split('.') and assign values, something like this:
var items = [
{
lookup_string: "User.UserProfile.name",
value: "John Smith"
},
{
lookup_string: "User.email",
value: "johnsmith#example.com"
},
];
var data = {};
items.forEach(function(item){
var lookup = item.lookup_string.split('.');
var lastKey;
lookup.map(function(key){
data[key] = {};
lastKey = key;
});
data[lastKey] = item.value;
});
console.log("items", items);
console.log("Results", data)
// In the end, data should look like this:
var desiredData = {
User: {
UserProfile: {
name: 'John Smith'
},
email: 'johnsmith#example.com'
}
};

New array of objects by comparing and pushing unique objects

I have 2 arrays of objects exclude and people, I want to create a new object by checking exclude properties against people properties and only adding objects in people that don't feature in exclude. So far my attempt is a little wild and wondering if someone can help make things a little better or offer a nicer solution?
Fiddle http://jsfiddle.net/kyllle/k02jw2j0/
JS
var exclude = [{
id: 1,
name: 'John'
}];
var peopleArr = [{
id: 1,
name: 'John'
}, {
id: 2,
name: 'James'
}, {
id: 3,
name: 'Simon'
}];
var myObj = [];
for (key in peopleArr) {
for (k in exclude) {
if (JSON.stringify(peopleArr[key]) != JSON.stringify(exclude[k])) {
console.log(peopleArr[key]);
myObj.push(peopleArr[key]);
}
}
}
console.log(myObj);
Under the assumption that exclude can have multiple items, I would use a combination of filter() and forEach() :
var newArray = peopleArr.filter(function(person) {
include = true;
exclude.forEach(function(exl) {
if (JSON.stringify(exl) == JSON.stringify(person)) {
include = false;
return;
}
})
if (include) return person;
})
forked fiddle -> http://jsfiddle.net/6c24rte8/
You repeat some JSON.stringify calls.
You can convert your arrays to JSON once, and then reuse it. Also, you can replace your push by Array.prototype.filter.
var excludeJson = exclude.map(JSON.stringify);
peopleArr = peopleArr.filter(function(x) {
return excludeJson.indexOf(JSON.stringify(x)) === -1;
});
Here is the working snippet:
var exclude = [{
id: 1,
name: 'John'
}];
var peopleArr = [{
id: 1,
name: 'John'
}, {
id: 2,
name: 'James'
}, {
id: 3,
name: 'Simon'
}];
var excludeJson = exclude.map(JSON.stringify);
peopleArr = peopleArr.filter(function(x) {
return excludeJson.indexOf(JSON.stringify(x)) === -1;
});
document.body.innerText = JSON.stringify(peopleArr);
This can be achieved with .filter and .findIndex
var myObj = peopleArr.filter(function(person){
var idx = exclude.findIndex(function(exc) { return person.id == exc.id && person.name == exc.name; });
return idx == -1; // means current person not found in the exclude list
});
I have explicitly compared the actual properties back to the original, there is nothing particularly wrong with your original way of comparing the stringified version (JSON.stringify(e) == JSON.stringify(x) could be used in my example)

How to convert a literal object into user defined object in javascript?

Writing a ton of web applications leveraging JSON/AJAX, I find myself returning tons literal javascript objects (JSON). For example, I may be request all the Cats from GetCats.asp. It would return:
[
{ 'id': 0, 'name': 'Persian' },
{ 'id': 1, 'name': 'Calico' },
{ 'id': 2, 'name': 'Tabby' }
]
Now, these are all Cat objects with behaviors. However, if I define a Cat object, function Cat() { }, I know of no EFFICIENT way to coax these literal objects into the behaviors of a user defined object.
I can do this by brute force of iterating through them and assigning functions, but it's not going to be pretty. Is there a nice, one line(or few), way of somehow "casting" this behavior onto these literal objects?
There's no getting around the fact that you will have to iterate through all of your simple objects and change them to a different kind of object. You cannot avoid the loop. That being said you could create a constructor that takes a simple object like this and copies those values into the new instance.
Like this:
function Cat(c) {
this.id = c.id;
this.name = c.name;
}
Cat.prototype.meow = function() {alert('meow');}
Cat.prototype.displayName= function() {alert(this.name);}
var cats = [
{ 'id': 0, 'name': 'Persian' },
{ 'id': 1, 'name': 'Calico' },
{ 'id': 2, 'name': 'Tabby' }
];
for (i=0,len=cats.length; i<len; i++) {
cats[i] = new Cat(cats[i]);
}
cats[0].meow(); // displays "meow"
cats[0].displayName(); // display "Persian"
If you're using the json2 parser (or another one with a compatible interface), you can simply provide a callback to replace the raw objects with custom objects:
var catSource = '[ { "id": 0, "name": "Persian" }, { "id": 1, "name": "Calico" }, { "id": 2, "name": "Tabby" } ]';
function Cat(id, name)
{
this.id = id;
this.name = name;
}
Cat.prototype =
{
toString: function()
{
return this.name;
}
};
function doStuff()
{
var cats = JSON.parse(catSource, function(key, val)
{
// some expression to detect the type of val
if ( val.id !== undefined && val.name !== undefined )
return new Cat(val.id, val.name);
return val;
});
alert(cats);
}
do you use prototype framework? if yes - here is an example
var cats = [
{id: 15, name: 'Tables', count:45 },
{id: 23, name: 'Chairs', count:34 }
];
var catsObjects = [];
cats.each(function(item){
var newObject = new Cat();
Object.extend(newObject, item);
catsObjects.push(newObject);
});
basically Array.each function is the same as "for i < Array.length"
and Object.extend is the same as property-by-property adding properties to newObject
Extending #Benry's answer.
I find that having an extend function to copy properties of one object onto another is essential. There is a semi-tradition for putting it onto Object; Object.extend() exists in many smaller libraries (NB: this is not the same as Object.prototype).
Object.extend = function ( take, give ) {
for (var k in give) {
if (give.hasOwnProperty(k)) {
take[k] = give[k];
}
}
return take;
}
This has the plus that you can use nice readable literal notation when writing your code:
function Cat (c) {
Object.extend( this, ( c || this.defaults ) );
}
Object.extend(Cat.prototype, {
meow : function() {
alert( 'Meow, my name is ' + this.name );
},
defaults : {
name : 'I have no name',
id : null
}
});
You can then create your army of cats quite simply:
var cats = [
{ 'id': 0, 'name': 'Persian' },
{ 'id': 1, 'name': 'Calico' },
{ 'id': 2, 'name': 'Tabby' }
];
for (i=0,len=cats.length; i<len; i++) {
cats[i] = new Cat( cats[i] );
}
cats[0].meow(); // displays "meow, my name is Persian"
Assign each object an appropriate prototype:
var list = [
{ 'id': 0, 'name': 'Persian' },
{ 'id': 1, 'name': 'Calico' },
{ 'id': 2, 'name': 'Tabby' }
];
for (obj in list)
{
obj.prototype = new Cat();
}

Categories