I try to create an object with a value for the last key. I just have an Array with the keys and the value but dont know how it will be possible to create an object without use references in javascript.
As far as I know there isnt a way to create a reference of a variable in javascript.
This is what i have:
var value = 'test';
var keys = ['this', 'is', 'a', 'test'];
This is what i want:
myObject: {
this : {
is: {
a : {
test : 'test'
}
}
}
}
Any idea how i can do this the best way in JavaScript ?
How about this...
const value = 'test'
const keys = ['this', 'is', 'a', 'test']
const myObject = keys.reduceRight((p, c) => ({ [c]: p }), value)
console.info(myObject)
Or, if you're not a fan of object literal key shortcuts and arrow functions...
keys.reduceRight(function(p, c) {
var o = {};
o[c] = p;
return o;
}, value);
See Array.prototype.reduceRight() - Polyfill if you need IE <= 8 support.
With this:
var curObj = myObject = {};
for(var i=0; i<keys.length-1; i++)
curObj = curObj[keys[i]] = {};
curObj[value] = keys[i];
Outputs:
{
this : {
is: {
a : {
Test : 'test'
}
}
}
}
As opposed to the other answers, this outputs exactly what you asked for.
Cheers
var o={}, c=o;
var value = 'Test';
var keys = 'this is a test'.split(' ');
for (var i=0; i<keys.length-1; ++i) c = c[keys[i]] = {};
c[keys[i]] = value;
console.log(JSON.stringify(o));
// {"this":{"is":{"a":{"test":"Test"}}}}
If you want the output like
{
this : {
is: {
a : 'test'
}
}
}
Do the following
var keys = ['this', 'is', 'a', 'test'];
var output = keys.reduceRight((p,c)=>({[c]:p}))
console.log(output)
And the output will be like
{ this: { is: { a: 'test' } } }
I have an object. I would like to modify the object (not clone it) by removing all properties except for certain specific properties. For instance, if I started with this object:
var myObj={
p1:123,
p2:321,
p3:{p3_1:1231,p3_2:342},
p4:'23423',
//....
p99:{p99_1:'sadf',p99_2:234},
p100:3434
}
and only want properties p1, p2, and p100, how can I obtain this object:
var myObj={
p1:123,
p2:321,
p100:3434
}
I understand how I could do this with brute force, but would like a more elegant solution.
This was the first hit when googling 'js keep only certain keys' so might be worth an update.
The most 'elegant' solution might just be using underscore.js
_.pick(myObj, 'p1', 'p2', 'p100')
Just re-initialise the object:
myObj = {
p1: myObj.p1,
p2: myObj.p2,
p100: myObj.p100
};
Another way is to delete certain properties, which is less effective:
var prop = ['p1', 'p2', 'p100'];
for (var k in myObj) {
if (prop.indexOf(k) < 0) {
delete myObj[k];
}
}
You could use this approach:
let result = (({ p1, p2, p100 }) => ({ p1, p2, p100 }))(myObj);
which I learned at https://stackoverflow.com/a/25554551/470749.
You could use delete:
for (var k in myObj) {
if (k !== 'p1' && k !== 'p2' && k !== 'p100') {
delete myObj[k];
}
}
An alternative to indexOf:
var take = /^p(1|2|100)$/;
for (var k in myObj) {
if (!take.test(k)) {
delete myObj[k];
}
}
Shorter:
var take = /^p(1|2|100)$/;
for (var k in myObj) {
take.test(k) || delete myObj[k];
}
Array to RegExp:
var take = [1, 2, 100];
take = new RegExp('^p(' + take.join('|') + ')$'); // /^p(1|2|100)$/
take.test('p1'); // true
take.test('p3'); // false
Useful in a function:
function take(o, ids) {
var take = new RegExp('^p(' + ids.join('|') + ')$');
for (var k in o) take.test(k) || delete o[k];
return o;
}
Usage:
take(myObj, [1, 2, 100]); // { p1: 123, p2: 321, p100: 3434 }
If you don't like regular expressions:
function take(o, keys) {
for (var k in o) contains(keys, k) || delete o[k];
return o;
}
function contains(array, value) {
var i = -1, l = array.length;
while (++i < l) if (array[i] === value) return true;
return false;
}
function prefix(array, prefix) {
var i = -1, l = array.length, output = [];
while (++i < l) output.push(prefix + array[i]);
return output;
}
Usage:
take(myObj, ['p1', 'p2', 'p100']);
// with a middleman :
var idsToTake = [1, 2, 100];
take(myObj, prefix(idsToTake, 'p'));
Lodash has a function called pick which does what you're describing. I know that including a library isn't ideal, but you can also cherry-pick functions when using bundles, etc.
var myObj={
p1:123,
p2:321,
p3:{p3_1:1231,p3_2:342},
p4:'23423',
//....
p99:{p99_1:'sadf',p99_2:234},
p100:3434
}
var newObj = _.pick(myObj, 'p1', 'p2', 'p100')
var myObj = {a: 1, b: 2, c:3};
function keepProps(obj, keep) {
for (var prop in obj) {
if (keep.indexOf( prop ) == -1) {
delete obj[prop];
}
}
}
keepProps(myObj, ['a', 'b']);
console.log(myObj);
http://jsfiddle.net/mendesjuan/d8Sp3/2/
An object stored in a variable named o :
var o = { a: 1, b: 2 };
A new reference to this object :
var p = o;
o and p both refer to the same object :
o // { a: 1, b: 2 }
p // { a: 1, b: 2 }
o === p // true
Let's update the object through o :
delete o.b;
o // { a: 1 }
p // { a: 1 }
Let's update the object through p :
p.b = 2;
o // { a: 1, b: 2 }
p // { a: 1, b: 2 }
As you can see, o and p are in sync.
Let's "reinitialize" o :
o = { a: o.a };
o and p now refer to different objects :
o // { a: 1 }
p // { a: 1, b: 2 }
o === p // false
Let's update the object stored in o :
o.c = 3;
o // { a: 1, c: 3 }
p // { a: 1, b: 2 }
Let's update the object stored in p :
delete p.a;
o // { a: 1, c: 3 }
p // { b: 2 }
As you can see, o and p are not in sync anymore.
The question is : do you want to keep both variables (o and p) synchronized? If so, the second code block of VisioN's answer is the right one, otherwise, choose the first code block.
You can code your own implementation of _.pick, and use it according to your needs.
Having this snippet of code as the base for the following cases:
const myObj={
p1:123,
p2:321,
p3:{p3_1:1231,p3_2:342},
p4:'23423',
//....
p99:{p99_1:'sadf',p99_2:234},
p100:3434
}
let properties= ['p1','p2', 'p3', 'p100'];
case 1:
You want a shallow copy (with references to vector values)
const myNewObj = properties.reduce((newObj,property)=>{newObj[property] = myObj[property]; return newObj}, {})
// if we modify the original vector value of 'p3' in `myObj` we will modify the copy as well:
myObj.p3.p3_1 = 99999999999;
console.log(myNewObj); // { p1: 123, p2: 321, p3: { p3_1: 99999999999, p3_2: 42 }, p100: 3434 }
case 2:
You want a deep copy (losing references to vector values)
You can just use JSON utilities to that matter.
const myNewObj2 = properties.reduce((newObj,property)=>{newObj[property] = JSON.parse(JSON.stringify(myObj[property])); return newObj},{})
// no matter how hard you modify the original object, you will create a new independent object
myObj.p3.p3_1 = 99999999999;
console.log(myNewObj2) // { p1: 123, p2: 321, p3: { p3_1: 1231, p3_2: 342 }, p100: 3434 }
Reusing case 2 with a function
You could implement a reducer to use it in different scenarios, like this one:
function reduceSelectedProps(origin, properties){
return (newObj,property)=>{
newObj[property] = JSON.parse(JSON.stringify(origin[property]));
return newObj
}
}
So you could have a more elegant reuse of it:
const myNewObj3 = properties.reduce(reduceSelectedProps(myObj, properties),{});
// no matter how hard you modify the original object, you will create a new independent object
myObj.p3.p3_1 = 99999999999;
console.log(myNewObj3) // { p1: 123, p2: 321, p3: { p3_1: 1231, p3_2: 342 }, p100: 3434 }
disclaimers:
This is only an example that does not handle Date, Set, Map or function values inside the properties. To deal with all these cases (and many others), it needs a really complex function with checks on the prototypes and all that stuff. At this point, consider reusing the work of other developers using any library that could do it. Lodash?
I suppose you could add a new method to the prototype:
if (!('keepOnlyTheseProps' in Object.prototype)) {
Object.prototype.keepOnlyTheseProps = function (arr) {
var keys = Object.keys(this);
for (var i = 0, l = keys.length; i < l; i++) {
if (arr.indexOf(keys[i]) < 0) delete this[keys[i]];
}
}
}
myObj.keepOnlyTheseProps(['p1', 'p2', 'p100']);
Fiddle.
Pass a map of whitelisted keys into an IIFE (immediately invoked function expression); not just elegant but also flexible IMO (especially if moved off into a function not unlike in Juan Mendes' answer)
var myObj={
p1:123,
p2:321,
p3:{p3_1:1231,p3_2:342},
p4:'23423',
//....
p99:{p99_1:'sadf',p99_2:234},
p100:3434
}
var myObj = (function(origObj, whiteListMap) {
for (var prop in origObj) {
if (!whiteListMap[prop]) {
delete origObj[prop];
}
}
return myObj;
})(myObj, {'p1': 1, 'p2': 1, 'p100': 1});
console.log(JSON.stringify(myObj)); //{"p1":123,"p2":321,"p100":3434}
You could create a view on your first object, some kind of proxy that would only keep the desired properties on sight.
For instance the following function will create a view that allows to both read and write the underlying object, keeping only the choosen properties.
You can make it readonly very easily, by just removing the setter.
You might also want to seal the proxy object, so that no later modification can me made to it.
function createView(obj, propList) {
var proxy = {};
for (var propIndex in propList) {
var prop=propList[propIndex];
Object.defineProperty(proxy, prop,
{ enumerable : true ,
get : getter.bind(obj,prop),
set : setter.bind(obj,prop) } );
}
return proxy;
}
function getter(prop) { return this[prop] ; }
function setter(prop, value) { return this[prop] = value ; }
An example of use would be :
var myObj={
p1:123,
p2:321,
p3:{p3_1:1231,p3_2:342},
p4:'23423',
p99:{p99_1:'sadf',p99_2:234},
p100:3434
};
var objView = createView(myObj, ['p1', 'p2', 'p100']);
Here, objView 'reflects' the desired properties of myObj.
You can look at the small jsbin i made here :
http://jsbin.com/munudewa/1/edit?js,console
results :
"on objView, p1:123 p2:321 p100:3434 and p4 (not in view) is : undefined"
"modifiying, on the view, p1 to 1000 and p2 to hello "
"on objView, p1:1000 p2:hello p100:3434 and p4 (not in view) is : undefined"
"modifiying, on the viewed object, p1 to 200 and p2 to bye "
"on objView, p1:200 p2:bye p100:3434 and p4 (not in view) is : undefined"
notice that :
1) you can overwrite an object by its view, only keeping desired properties.
2) you can save in a hidden property / in a closure, the original object, so you can later change the properties you expose.
I Made this short solution for case where I have an array with objects.
so consider the array below?
arr=[{"a1":"A1","b1":"B1"},{"a1":"Z1","b1":"X1"}];
console.log(arr);
I want to keep only "b1" properties of all objects.
You can use map() and delete for that as follows:
arr=[{"a1":"A1","b1":"B1"},{"a1":"Z1","b1":"X1"}];
arr=arr.map(function(d){
delete d["a1"];
return d;
});
console.log(arr);
result is an array with objects but only "b1" properties.
just a single line of pure js code
var myObj={
p1:123,
p2:321,
p3:{p3_1:1231,p3_2:342},
p4:'23423',
//....
p99:{p99_1:'sadf',p99_2:234},
p100:3434
}
Object.keys(myObj).forEach(key => { if(!["p1","p2","p100"].includes(key)) delete myObj[key]; })
This question achieves kinda the opposite of what I'm trying to do. Basically, I have this object:
var a = {
b: {
c: 'Foo'
}
}
What I need to do is set the value of c given the string 'b.c'. Unfortunately, I can't do this:
a['b.c'] = 'Bar'
As far as I can tell, the question above doesn't get me anywhere close as it just copies the values of the object properties so they can be read. It doesn't help me set the values of the object properties, however. Here's what I have so far:
var key = 'b.c'.split('.');
for (var i = 0; i < key.length; i++) {
// do something
}
Here's a functional way, is this what you need? It doesn't use the particular a[string] syntax but a function where you can pass the object string and the value to set:
var obj = { foo: { bar: { lol: { lulz: 69 } } } };
function setProp(obj, prop, value) {
var props = prop.split('.');
return [obj].concat(props).reduce(function(a,b,i) {
return i == props.length ? a[b] = value : a[b];
});
}
setProp(obj, 'foo.bar.lol.lulz', 'hello world');
console.log(obj.foo.bar.lol.lulz); //=> "hello world"
You have to intercept the last iteration of the loop, and from there assign instead of redefining your temp variable.
I adapted the answer to the question you linked to assign the value 2 to a.b.c:
var a = {
"b" : {
"c" : 1
}
}
var n = "b.c".split(".");
var x = a;
for(var i = 0; i < n.length; i++){
if(i == n.length-1) {
x[n[i]] = 2;
} else {
x = x[n[i]];
}
}
http://jsfiddle.net/Fuhbb/
This is pretty much what #Ian is doing in his jsfiddle. Intead of an if, he loops one step less, then deals with the assignment after the loop.
For the record, I eventually figured out another way to do this:
function setProperty(obj, props, val) {
if (obj[props[0]] instanceof Object) {
setProperty(obj[props[0]], props.slice(1, props.length), val);
return;
}
obj[props[0]] = val;
}
Call it like this:
a = {
b: {
c: 'Foo'
}
};
var whatever = 'b.c';
var props = whatever.split('.');
setProperty(a, props, 'Bar');
say I have an object like this:
a : {
a1 : {
a2: true
}
}
and I have all the path saved in an array:
[a1, a2]
If I want to assign value to a["a1"]["a2"], it is easy:
a["a1"]["a2"] = true;
However when I have a 3 level path like this:
[a1, a2, a3]
I have to manually write the code like this:
a["a1"]["a2"]["a3"] = true;
Is there a way to automatically handle any level of paths so that I don't have to make it explicit for every single case?
Note that "a" can be quite complex so I only want to assign value to this specific element and without touching the rest.
You could iteratively traverse the object with the path like so:
function setDeepProperty(obj, path, value)
{
var curr = obj;
for (var depth = 0; depth < path.length - 1; depth++)
{
curr = curr[path[depth]];
}
curr[path[path.length - 1]] = value;
}
This assumes that the path is valid. Ensure that path[depth] in curr if necessary. The last step in the traversal is done outside of the loops because it would be setting curr to a primitive type instead of referencing an array (as we desire) meaning it wouldn't change the original. Then, as per your example:
var arr = {a1: {a2: { a3: false }}};
setDeepProperty(arr, ["a1", "a2", "a3"], true);
Note here that the nodes in the path are strings.
There are several ways you could access the properties:
Use a loop:
var obj = {
a1 : {
a2: { a3: 'test' }
}
},
i = 0,
keyPath = ['a1', 'a2', 'a3'],
len = keyPath.length;
for (; i < len; i++) {
obj = obj[keyPath[i]];
}
console.log(obj);
With eval (I don't recommend this however):
var obj = {
a1 : {
a2: { a3: 'test' }
}
};
var value = eval('obj.' + keyPath.join('.'));
console.log(value);
You could use the same approach to set a property at a specific key path:
function setProperty(obj, keyPath, value) {
var i = 0,
len = keyPath.length - 1;
for (; i < len; i++) {
obj = obj[keyPath[i]];
}
obj[keyPath[i]] = value;
}
All are elegant solutions, my 2 cents with recursion:-
Test Here
var a = {
a1: {
a2: {
a3: false
}
}
};
var path = ['a1', 'a2', 'a3'];
var valueToSet = true;
setValue(0, a);
function setValue(level, ob) {
var prop = path[level];
if (!ob.hasOwnProperty(prop)) {
return;
}
if (level == (path.length - 1)) {
ob[prop] = valueToSet;
return;
}
return setValue(level + 1, ob[prop]);
}
console.log(a);
You have 2 possibilities:
the dreaded eval(). I refuse giving code for that
an in-out loop:
Code:
var a={
a1 : {
a2 : {
a3: false
}
}
};
var idx=["a1", "a2", "a3"];
function recReplace(o, i, v) {
var ii=i.shift();
if (i.length==0)
o[ii]=v;
else
o[ii]=recReplace(o[ii],i,v);
return o;
}
b=recReplace(a,idx,true); //or: a=recReplace(a,idx,true);
Sure, it's a simple loop:
var a = {a1:{a2:{}}};
var path = ["a1", "a2", "a3"];
for (var o=a, i=0; i<path.length-1; i++)
o = o[path[i]]; // notice that this will throw exception
// when the objects do not exist already
o[path[i]] = true;
How can I reach into a object using an array and set a value - preferably without using eval, doing something like object[eval(["key", "deepkey"].split("")) = "newvalue"?
Doing it manually, I would just do object.key.deepkey = "newvalue", but again, I need to do this using an array to reach into the right property.
The object for reference:
object = {
key: {
deepKey: "value"
}
}
You can use a recursive function to step through each level of the array (or object) like so:
function val(array, indices) {
if(indices.length > 1) {
var idx = indices.shift();
return val(array[idx], indices);
}
else {
return array[indices.shift()];
}
}
var obj = { a: { b: 'c' } };
//result is 'c'
var result = val(obj, ['a', 'b']);
If you want to get an object reference, simply specify the second arg only up to that:
var obj = {
a: {
b: {
c: 'foo'
}
}
};
var ref = val(obj, ['a', 'b']);
//ref is now obj.a.b, so you can do something like...
ref.x = 'bar';
console.dir(ref); //outputs something like { c: 'foo', x: 'bar' }
You can write array type syntax as. jsfiddle
object = {
key: {
deepKey: "value"
}
}
object['key']['deepkey']='newvalue'
if you have keys in array you can do this
var keys = ['key','deepkey'];
var obj = object;
for(var k =0; k <keys.length-1; k++){
obj= obj[keys[k]];
}
obj[keys[k]] = 'newvalue'
You can take the function from this question and rework it to access properties of an object.
http://jsfiddle.net/jbabey/Mu4rP/
var getPropByName = function (propName, context) {
var namespaces = propName.split('.');
for(var i = 0; i < namespaces.length; i++) {
context = context[namespaces[i]];
}
return context;
};
var myObject = {
someKey: {
deepKey: "value"
}
};
myObject.someKey.deepKey; // "value"
getPropByName('someKey.deepKey', myObject); "value"
An alternative could be using Array.map this way:
function deepkey(obj,keys,set){
var i=1
,kys = keys.split('.')
,exist = kys.map( function(k){
var prev = this[i-1], isobj = prev.constructor === Object;
this.push( isobj && k in prev ? prev[k] : prev);
return (i++,this[i-1]);},
[obj]
)
,x = exist[exist.length-2];
if (x && x.constructor === Object && set){
x[kys[kys.length-1]] = set;
}
return x[kys.pop()] || null;
}
// usage
var obj = { a:{ b:{ c:1, cc:{ d:{ e:{ a:1,b:2,c:3 } } } } } };
// assign [1,2,3,4,5] to obj.a.b.cc.d.e.b
console.log(deepkey(obj,'a.b.cc.d.e.b',[1,2,3,4,5])); //=> [1,2,3,4,5]
// get obj.a.b.cc.d.e.b[2]
console.log(deepkey(obj,'a.b.cc.d.e.b')[2]); //=> 3
// get non existing path obj.a.b.c.d.e.b
console.log(deepkey(obj,'a.b.c.d.e.b')); //=> null