This question already has answers here:
Accessing nested JavaScript objects and arrays by string path
(44 answers)
Closed 4 years ago.
Suppose I have an object
var obj = {
subObj : {
value : 1
}
};
Is there any way to get obj.subObj.value using a complex property-name string that goes to sub objects?
Example (that doesn't work)
var attr = "subObj.value";
return obj[attr];
No you can't.
You can split and loop over every attr.
var obj = {
subObj : {
value : 1
}
};
var attr = "subObj.value";
var result = obj;
attr.split('.').forEach((c) => result = result[c]);
console.log(result);
Or you can use reduce:
var obj = {
subObj : {
value : 1
}
};
var attr = "subObj.value";
var result = attr.split('.').reduce((a, c) => a[c], obj);
console.log(result);
There is no notation to do this in JavaScript but you could use something like this:
var obj = {
subObj : {
value : 1
}
};
var attr = "subObj.value";
var result = attr.split(".").reduce((a, c) => a[c], obj);
console.log(result)
Related
This question already has answers here:
Add a property to a JavaScript object using a variable as the name? [duplicate]
(14 answers)
Closed 3 years ago.
Seeking the below result with dynamic list of object using javascript. Defined approach getting me undefined index of value.
Javascript Code
var obj = {};
var obj_ = {
"clist_1": "abc",
"clist_2": "def",
"branch_1": "efg"
}
for (let key in obj_) {
if (key.includes("clist_")) {
let num = key.replace(/^\D+/g, '');
obj[num] = obj_.key;
}
}
console.log(obj)
Desired Result
{
"1": "abc",
"2": "def"
}
You can use bracket notation. You need to do obj_[key] and not obj_.key.
var obj = {};
var obj_ = {
"clist_1": "abc",
"clist_2": "def",
"branch_1": "efg"
}
for (let key in obj_) {
if (key.includes("clist_")) {
let num = key.replace(/^\D+/g, '');
obj[num] = obj_[key];
}
}
console.log(obj)
You need a property accessor in bracket notation for the key.
obj[num] = obj_[key];
// ^^^^^
Then you could use startsWith instead of includes for checking the starting part of a string.
var obj = {};
var obj_ = {
"clist_1": "abc",
"clist_2": "def",
"branch_1": "efg"
}
for (let key in obj_) {
if (key.startsWith("clist_")) {
let num = key.replace(/^\D+/g, '');
obj[num] = obj_[key];
}
}
console.log(obj);
Alternative chain way:
var obj = {};
var obj_ = {
"clist_1": "abc",
"clist_2": "def",
"branch_1": "efg"
};
Object.keys(obj_)
.map(key=>key.replace(/^\D+/g, ''))
.map(key=>obj[key]=obj_[key]);
console.log(obj);
You can do:
const obj_ = {"clist_1": "abc","clist_2": "def","branch_1": "efg"};
const obj = Object.keys(obj_)
.filter(k => k.startsWith('clist_'))
.reduce((a, c) => (a[c.match(/\d+/g)] = obj_[c], a), {});
console.log(obj);
This question already has answers here:
Convert string in dot notation to get the object reference [duplicate]
(6 answers)
Closed 6 years ago.
If I have a base object which is always the same: project but sometimes I have to access its fields dynamically, how can I access its fields when it can be 1 or more nested objects for example:
function (myPath){
return project[myPath];
}
This works when using project["oneField"] in myPath("oneField")
but it does not work when it is nested to or more levels:
myPath("one.two.fieldName") does not work: project["one.two.fieldName"]
Neither like this: project."one.two.fieldName"
You could do it like this (ES6):
function getVal(object, path){
return path.split('.').reduce ( (res, prop) => res[prop], object );
}
// sample data
var object = { one: { two: { fieldName: 'gotcha' } } };
// get inner field value
console.log( getVal(object, 'one.two.fieldName') );
You can further extend this function to also support square brackets:
function getVal(object, path){
const regex = /(?<=\[)(?!")[^\]]+|(?<=\[")[^"]+|[^."[\]]+/g;
return path.match(regex).reduce ( (res, prop) => res[prop], object );
}
let object = { 'a': [{ 'b': { 'c': 3 } }] };
console.log( getVal(object, 'a[0].b["c"]') );
option #1 (not recommended) - use the eval function:
var projects = {
a : {
b : {
c : 1
}
}
}
function get(myPath) {
debugger;
return eval("projects." + myPath)
}
console.log(get('a.b.c'))
option #2 - split by . and go over the elements in the object:
var projects = {
a: {
b : {
c : '1'
}
}
}
function get(path) {
if (path.indexOf('.')) {
subs = path.split(".")
ret = projects;
for (var i = 0; i < subs.length; i++) {
ret = ret[subs[i]]
}
return ret;
} else {
return projects[path];
}
}
console.log(get('a'))
console.log(get('a.b'))
console.log(get('a.b.c'))
Usually if I'm doing something like this, I'll use a recursive function:
var data = {a: {b: {c: 5}}};
function getValue(obj, key) {
var parts = key.split('.');
return obj
&& (parts.length === 1
&& obj[key] || getValue(obj[parts[0]], parts.slice(1).join('.')))
|| null;
}
console.log(getValue(data, "a.b.c"));
JSFiddle
It's a bit concisely written, but basically if it has a key with a dot in it, it'll call it down a level. If it ever gets an obj that doesn't exist, it'll return null. Otherwise, once it's down to the last level, it'll return the value it found.
A more expanded, maybe easier to understand, version is:
function getValue(obj, key) {
var parts = key.split('.');
if (!obj) {
return null;
} else if (parts.length === 1) {
return obj[key];
}
return getValue(obj[parts.slice(1).join('.')], key);
}
I have the string "presentation.launchBehavior.newWindow", which needs to be de-serialized into object:
var obj = {presentation: {launchBehavior: 'newWindow'}}
The last string after dot should be value, while other strings should be converted to strings. I've written the function to do that:
function des(obj, property, index, ar) {
var isLastCall = ar.length - 1 === index;
if (isLastCall) {
return;
}
var isNextLastCall = ar.length - 2 === index;
if (isNextLastCall) {
obj[property] = ar[ar.length - 1];
return obj;
} else {
obj[property] = {};
return obj[property];
}
}
var obj = {};
"presentation.launchBehavior.newWindow".split(".").reduce(des, obj);
obj.presentation.launchBehavior // newWindow
It's working, but I'd like to improve it to return the resulting object instead of having to create a variable outside the function. How can I do that? Maybe internals of the function can be improved as well.
You can use reduceRight:
const des = str => str.split(".").reduceRight( (prev, key) => ({[key]: prev}) );
console.log(des("presentation.launchBehavior.newWindow"));
Here is how I would do it.
function des(str) {
var props = (typeof str === "string") ? str.split('.') : str;
return props.length > 1 ? {[props[0]]: des(props.slice(1))}
: props[0];
}
console.log(des("presentation.launchBehavior.newWindow"));
It uses array computed properties (var o = {[key]: val}). It may not work on old browsers. For old browsers, you will have to first create the object assign then property (var o = {}; o[key] = val;).
EDIT: SOLVED See final solution below
EDIT: The value names are not the same
object 1
var obj1 = {
val1a : 4,
val2a : 3 ,
val3a : 7
}
object 2 with arrays
var obj2 = {
val1b : [one, two, three],
val2b : [oneA, twoA, threeA],
val3b : [oneB]
}
I am trying to do is the following
if(obj1.val1a === obj2.val1b.length){
// code
}
but I do not want it to be so specific. Is there a way to loop over each object and return the values of obj2 that do not match obj1
SOLUTION with underScore
function checkData(obj1, obj2) {
result = []
var keys_obj1 = Object.keys( obj1)
var keys_obj2 = Object.keys( obj2)
_.each(keys_obj1, function(num, i){
if(obj1[keys_obj1[i]].length !== obj2[keys_obj2[i]]) {
result.push(keys_obj1[i]);
}
})
return result;
}
The best way to do this is if both objects have the same name (for a given pair), then using The For/In Loop iterate over one of them and return the value of both, and then compare.
Remade the fiddle using Object.keys to make an array of keys for both objects, now it works even when the keys aren't the same (following the object index)
var obj1 = {
val1a : 4,
val2a : 3 ,
val3a : 7
}
var obj2 = {
val1b : ['one', 'two', 'three'],
val2b : ['oneA', 'twoA', 'threeA'],
val3b : ['oneB']
}
var keys_obj1 = Object.keys( obj1 )
var keys_obj2 = Object.keys( obj2 )
for (i = 0; i < keys_obj1.length; i++) {
console.log(keys_obj1[i]+'-'+obj1[keys_obj1[i]])
console.log(keys_obj2[i]+'-'+obj2[keys_obj2[i]].length);
if(obj1[keys_obj1[i]] === obj2[keys_obj2[i]].length) {
//match
}
}
console output :
"val1a-4"
"val1b-3"
"val2a-3"
"val2b-3"
"val3a-7"
"val3b-1"
If your model is not the definitive one, you could use an array of object like :
var object = [
{table : [one, two, three], length : 3},
{table : [one, two, three], length : 4},
... ];
and compare values using :
object[i].table.length === object[i].length
It's a little bit different from your model
but i hope it may helps.
Quite long-winded, but the only way I could think to do it:
// for each object you are going to...
function pullData(obj) {
var out = {};
var token;
// grab the keys
var keys = Object.keys(obj).map(function (el) {
// token is the character at the end of the key
// the key that is returned is the key without the token
token = el.slice(-1)
return el.slice(0, 4);
});
// for each key, add it to the output object
for (var i = 0, l = keys.length; i < l; i++) {
out[keys[i]] = obj[keys[i] + token]
}
// return an object containing the data and the token
return {data: out, token: token};
}
// take both the output from both objects being parsed
function isNotMatch(obj1, obj2) {
var out = [];
// loop over the first object using the now identical keys
for (var p in obj1.data) {
// check the values and lengths
// if they're not the same push the key and the token as a string
// to the output array
if (obj1.data[p] !== obj2.data[p].length) {
out.push(p + obj2.token);
}
}
// return the array of non-matches
return out;
}
isNotMatch(pullData(obj1), pullData(obj2));
DEMO
UPDATED:
Maybe something like this. You would have to make sure the property names are the same:
var obj1 = {
val1a : 4,
val2a : 3 ,
val3a : 7
};
var obj2 = {
val1a : ['one', 'two', 'three'],
val2a : ['oneA', 'twoA', 'threeA'],
val3a : ['oneB']
};
for(var name in obj1) {
if(obj1[name] === obj2[name].length) {
alert("match");
}
}
This question already has answers here:
Accessing nested JavaScript objects and arrays by string path
(44 answers)
Closed 9 years ago.
I need to convert an object with cannonical properties into object with nested properties, splited by '.'
Example:
From:
obj['a.b.c'] = 123;
to:
obj.a.b.c = 123;
Any elegant solutions?
Or maybe there is a solution in ExtJS to make form.getValues() to return an array of fields grouped by names like fieldname[1] or fieldname.1?
Have a look at the private method in ClassManager "createNamespaces". It's essentially what you need to do, except root shouldn't default to global, it should default to your object:
function setValue(o, key, value) {
var root = o,
parts = key.split('.'),
ln = parts.length,
part, i;
for (i = 0; i < ln; i++) {
part = parts[i];
if (typeof part != 'string') {
root = part;
} else {
if (!root[part]) {
root[part] = (i === ln - 1) ? value : {};
}
root = root[part];
}
}
}
var o = {};
setValue(o, 'a.b.c', 123);
console.log(o.a.b.c);