Object value in memory (stack?) in recursive function? - javascript

I found a good piece of code on SO that I only partially understand. If you could help me understand what is happening with myObj in the second loop of this recursive function when the function no longer points to myObj (instead it points to tmpObj yet it still adds onto it.
var myObj = {};
function addProps(obj, arr, val) { // in the first run obj = myObj
if (typeof arr == 'string')
arr = arr.split(".");
obj[arr[0]] = obj[arr[0]] || {};
var tmpObj = obj[arr[0]];
if (arr.length > 1) {
arr.shift();
addProps(tmpObj, arr, val); // whilst recursing, tmpObj becomes obj (as function parameter), so why myObj keeps being changed as well?
}
else
obj[arr[0]] = val;
return obj;
};
addProps(myObj, 'sub1.sub2.propA', 1);
Link to the original piece of code :
https://stackoverflow.com/a/34205057

when you have a nested object and mutate the inner one, there is no copying involved... the outer one still points to the same (now mutated) inner object, e.g.:
const myObj = {a: {b: 'c'}}
const tmpObj = myObj.a
tmpObj.b = 'd'
console.log('original mutated after changing the inner object:', myObj)
const copyObj = Object.assign({}, myObj.a) // or {...myObj.a}
copyObj.b = 'e'
console.log('original NOT mutated after changing a copy:', myObj)

Related

Js how to set an item at a nested index in an empty array [duplicate]

I hope someone can help me with this Javascript.
I have an Object called "Settings" and I would like to write a function that adds new settings to that object.
The new setting's name and value are provided as strings. The string giving the setting's name is then split by the underscores into an array. The new setting should get added to the existing "Settings" object by creating new nested objects with the names given by each part of the array, except the last part which should be a string giving the setting's value. I should then be able to refer to the setting and e.g. alert its value. I can do this in a static way like this...
var Settings = {};
var newSettingName = "Modules_Video_Plugin";
var newSettingValue = "JWPlayer";
var newSettingNameArray = newSettingName.split("_");
Settings[newSettingNameArray[0]] = {};
Settings[newSettingNameArray[0]][newSettingNameArray[1]] = {};
Settings[newSettingNameArray[0]][newSettingNameArray[1]][newSettingNameArray[2]] = newSettingValue;
alert(Settings.Modules.Mediaplayers.Video.Plugin);
... the part that creates the nested objects is doing this ...
Settings["Modules"] = {};
Settings["Modules"]["Video"] = {};
Settings["Modules"]["Video"]["Plugin"] = "JWPlayer";
However, as the number of parts that make up the setting name can vary, e.g. a newSettingName could be "Modules_Floorplan_Image_Src", I'd like to do this dynamically using a function such as...
createSetting (newSettingNameArray, newSettingValue);
function createSetting(setting, value) {
// code to create new setting goes here
}
Can anyone help me work out how to do this dynamically?
I presume there has to be a for...loop in there to itterate through the array, but I haven't been able to work out a way to create the nested objects.
If you've got this far thanks very much for taking the time to read even if you can't help.
Put in a function, short and fast (no recursion).
var createNestedObject = function( base, names ) {
for( var i = 0; i < names.length; i++ ) {
base = base[ names[i] ] = base[ names[i] ] || {};
}
};
// Usage:
createNestedObject( window, ["shapes", "triangle", "points"] );
// Now window.shapes.triangle.points is an empty object, ready to be used.
It skips already existing parts of the hierarchy. Useful if you are not sure whether the hierarchy was already created.
Or:
A fancier version where you can directly assign the value to the last object in the hierarchy, and you can chain function calls because it returns the last object.
// Function: createNestedObject( base, names[, value] )
// base: the object on which to create the hierarchy
// names: an array of strings contaning the names of the objects
// value (optional): if given, will be the last object in the hierarchy
// Returns: the last object in the hierarchy
var createNestedObject = function( base, names, value ) {
// If a value is given, remove the last name and keep it for later:
var lastName = arguments.length === 3 ? names.pop() : false;
// Walk the hierarchy, creating new objects where needed.
// If the lastName was removed, then the last object is not set yet:
for( var i = 0; i < names.length; i++ ) {
base = base[ names[i] ] = base[ names[i] ] || {};
}
// If a value was given, set it to the last name:
if( lastName ) base = base[ lastName ] = value;
// Return the last object in the hierarchy:
return base;
};
// Usages:
createNestedObject( window, ["shapes", "circle"] );
// Now window.shapes.circle is an empty object, ready to be used.
var obj = {}; // Works with any object other that window too
createNestedObject( obj, ["shapes", "rectangle", "width"], 300 );
// Now we have: obj.shapes.rectangle.width === 300
createNestedObject( obj, "shapes.rectangle.height".split('.'), 400 );
// Now we have: obj.shapes.rectangle.height === 400
Note: if your hierarchy needs to be built from values other that standard objects (ie. not {}), see also TimDog's answer below.
Edit: uses regular loops instead of for...in loops. It's safer in cases where a library modifies the Array prototype.
function assign(obj, keyPath, value) {
lastKeyIndex = keyPath.length-1;
for (var i = 0; i < lastKeyIndex; ++ i) {
key = keyPath[i];
if (!(key in obj)){
obj[key] = {}
}
obj = obj[key];
}
obj[keyPath[lastKeyIndex]] = value;
}
Usage:
var settings = {};
assign(settings, ['Modules', 'Video', 'Plugin'], 'JWPlayer');
My ES2015 solution. Keeps existing values.
const set = (obj, path, val) => {
const keys = path.split('.');
const lastKey = keys.pop();
const lastObj = keys.reduce((obj, key) =>
obj[key] = obj[key] || {},
obj);
lastObj[lastKey] = val;
};
Example:
const obj = {'a': {'prop': {'that': 'exists'}}};
set(obj, 'a.very.deep.prop', 'value');
console.log(JSON.stringify(obj));
// {"a":{"prop":{"that":"exists"},"very":{"deep":{"prop":"value"}}}}
Using ES6 is shorten. Set your path into an array.
first, you have to reverse the array, to start filling the object.
let obj = ['a','b','c'] // {a:{b:{c:{}}}
obj.reverse();
const nestedObject = obj.reduce((prev, current) => (
{[current]:{...prev}}
), {});
Another recursive solution:
var nest = function(obj, keys, v) {
if (keys.length === 1) {
obj[keys[0]] = v;
} else {
var key = keys.shift();
obj[key] = nest(typeof obj[key] === 'undefined' ? {} : obj[key], keys, v);
}
return obj;
};
Example usage:
var dog = {bark: {sound: 'bark!'}};
nest(dog, ['bark', 'loudness'], 66);
nest(dog, ['woff', 'sound'], 'woff!');
console.log(dog); // {bark: {loudness: 66, sound: "bark!"}, woff: {sound: "woff!"}}
I love this ES6 immutable way to set certain value on nested field:
const setValueToField = (fields, value) => {
const reducer = (acc, item, index, arr) => ({ [item]: index + 1 < arr.length ? acc : value });
return fields.reduceRight(reducer, {});
};
And then use it with creating your target object.
const targetObject = setValueToField(['one', 'two', 'three'], 'nice');
console.log(targetObject); // Output: { one: { two: { three: 'nice' } } }
Lodash has a _.set method to achieve this
let obj = {}
_.set(obj, ['a', 'b', 'c', 'd'], 'e')
or
_.set(obj, 'a.b.c.d', 'e')
// which generate the following object
{
"a": {
"b": {
"c": {
"d": "e"
}
}
}
}
Here is a simple tweak to jlgrall's answer that allows setting distinct values on each element in the nested hierarchy:
var createNestedObject = function( base, names, values ) {
for( var i in names ) base = base[ names[i] ] = base[ names[i] ] || (values[i] || {});
};
Hope it helps.
Here is a functional solution to dynamically create nested objects.
const nest = (path, obj) => {
const reversedPath = path.split('.').reverse();
const iter = ([head, ...tail], obj) => {
if (!head) {
return obj;
}
const newObj = {[head]: {...obj}};
return iter(tail, newObj);
}
return iter(reversedPath, obj);
}
Example:
const data = {prop: 'someData'};
const path = 'a.deep.path';
const result = nest(path, data);
console.log(JSON.stringify(result));
// {"a":{"deep":{"path":{"prop":"someData"}}}}
Inspired by ImmutableJS setIn method which will never mutate the original. This works with mixed array and object nested values.
function setIn(obj = {}, [prop, ...rest], value) {
const newObj = Array.isArray(obj) ? [...obj] : {...obj};
newObj[prop] = rest.length ? setIn(obj[prop], rest, value) : value;
return newObj;
}
var obj = {
a: {
b: {
c: [
{d: 5}
]
}
}
};
const newObj = setIn(obj, ["a", "b", "c", 0, "x"], "new");
//obj === {a: {b: {c: [{d: 5}]}}}
//newObj === {a: {b: {c: [{d: 5, x: "new"}]}}}
Appreciate that this question is mega old! But after coming across a need to do something like this in node, I made a module and published it to npm.
Nestob
var nestob = require('nestob');
//Create a new nestable object - instead of the standard js object ({})
var newNested = new nestob.Nestable();
//Set nested object properties without having to create the objects first!
newNested.setNested('biscuits.oblong.marmaduke', 'cheese');
newNested.setNested(['orange', 'tartan', 'pipedream'], { poppers: 'astray', numbers: [123,456,789]});
console.log(newNested, newNested.orange.tartan.pipedream);
//{ biscuits: { oblong: { marmaduke: 'cheese' } },
orange: { tartan: { pipedream: [Object] } } } { poppers: 'astray', numbers: [ 123, 456, 789 ] }
//Get nested object properties without having to worry about whether the objects exist
//Pass in a default value to be returned if desired
console.log(newNested.getNested('generic.yoghurt.asguard', 'autodrome'));
//autodrome
//You can also pass in an array containing the object keys
console.log(newNested.getNested(['chosp', 'umbridge', 'dollar'], 'symbols'));
//symbols
//You can also use nestob to modify objects not created using nestob
var normalObj = {};
nestob.setNested(normalObj, 'running.out.of', 'words');
console.log(normalObj);
//{ running: { out: { of: 'words' } } }
console.log(nestob.getNested(normalObj, 'random.things', 'indigo'));
//indigo
console.log(nestob.getNested(normalObj, 'improbable.apricots'));
//false
Inside your loop you can use lodash.set and will create the path for you:
...
const set = require('lodash.set');
const p = {};
const [type, lang, name] = f.split('.');
set(p, [lang, type, name], '');
console.log(p);
// { lang: { 'type': { 'name': '' }}}
try using recursive function:
function createSetting(setting, value, index) {
if (typeof index !== 'number') {
index = 0;
}
if (index+1 == setting.length ) {
settings[setting[index]] = value;
}
else {
settings[setting[index]] = {};
createSetting(setting, value, ++index);
}
}
I think, this is shorter:
Settings = {};
newSettingName = "Modules_Floorplan_Image_Src";
newSettingValue = "JWPlayer";
newSettingNameArray = newSettingName.split("_");
a = Settings;
for (var i = 0 in newSettingNameArray) {
var x = newSettingNameArray[i];
a[x] = i == newSettingNameArray.length-1 ? newSettingValue : {};
a = a[x];
}
I found #jlgrall's answer was great but after simplifying it, it didn't work in Chrome. Here's my fixed should anyone want a lite version:
var callback = 'fn.item1.item2.callbackfunction',
cb = callback.split('.'),
baseObj = window;
function createNestedObject(base, items){
$.each(items, function(i, v){
base = base[v] = (base[v] || {});
});
}
callbackFunction = createNestedObject(baseObj, cb);
console.log(callbackFunction);
I hope this is useful and relevant. Sorry, I've just smashed this example out...
You can define your own Object methods; also I'm using underscore for brevity:
var _ = require('underscore');
// a fast get method for object, by specifying an address with depth
Object.prototype.pick = function(addr) {
if (!_.isArray(addr)) return this[addr]; // if isn't array, just get normally
var tmpo = this;
while (i = addr.shift())
tmpo = tmpo[i];
return tmpo;
};
// a fast set method for object, put value at obj[addr]
Object.prototype.put = function(addr, val) {
if (!_.isArray(addr)) this[addr] = val; // if isn't array, just set normally
this.pick(_.initial(addr))[_.last(addr)] = val;
};
Sample usage:
var obj = {
'foo': {
'bar': 0 }}
obj.pick('foo'); // returns { bar: 0 }
obj.pick(['foo','bar']); // returns 0
obj.put(['foo', 'bar'], -1) // obj becomes {'foo': {'bar': -1}}
A snippet for those who need to create a nested objects with support of array keys to set a value to the end of path. Path is the string like: modal.product.action.review.2.write.survey.data. Based on jlgrall version.
var updateStateQuery = function(state, path, value) {
var names = path.split('.');
for (var i = 0, len = names.length; i < len; i++) {
if (i == (len - 1)) {
state = state[names[i]] = state[names[i]] || value;
}
else if (parseInt(names[i+1]) >= 0) {
state = state[names[i]] = state[names[i]] || [];
}
else {
state = state[names[i]] = state[names[i]] || {};
}
}
};
Set Nested Data:
function setNestedData(root, path, value) {
var paths = path.split('.');
var last_index = paths.length - 1;
paths.forEach(function(key, index) {
if (!(key in root)) root[key] = {};
if (index==last_index) root[key] = value;
root = root[key];
});
return root;
}
var obj = {'existing': 'value'};
setNestedData(obj, 'animal.fish.pet', 'derp');
setNestedData(obj, 'animal.cat.pet', 'musubi');
console.log(JSON.stringify(obj));
// {"existing":"value","animal":{"fish":{"pet":"derp"},"cat":{"pet":"musubi"}}}
Get Nested Data:
function getNestedData(obj, path) {
var index = function(obj, i) { return obj && obj[i]; };
return path.split('.').reduce(index, obj);
}
getNestedData(obj, 'animal.cat.pet')
// "musubi"
getNestedData(obj, 'animal.dog.pet')
// undefined
Try this: https://github.com/silkyland/object-to-formdata
var obj2fd = require('obj2fd/es5').default
var fd = obj2fd({
a:1,
b:[
{c: 3},
{d: 4}
]
})
Result :
fd = [
a => 1,
b => [
c => 3,
d => 4
]
]
Here is a decomposition to several useful functions, that each preserve existing data. Does not handle arrays.
setDeep: Answers question. Non-destructive to other data in the object.
setDefaultDeep: Same, but only sets if not already set.
setDefault: Sets a key if not already set. Same as Python's setdefault.
setStructure: Helper function that builds the path.
// Create a nested structure of objects along path within obj. Only overwrites the final value.
let setDeep = (obj, path, value) =>
setStructure(obj, path.slice(0, -1))[path[path.length - 1]] = value
// Create a nested structure of objects along path within obj. Does not overwrite any value.
let setDefaultDeep = (obj, path, value) =>
setDefault(setStructure(obj, path.slice(0, -1)), path[path.length - 1], value)
// Set obj[key] to value if key is not in object, and return obj[key]
let setDefault = (obj, key, value) =>
obj[key] = key in obj ? obj[key] : value;
// Create a nested structure of objects along path within obj. Does not overwrite any value.
let setStructure = (obj, path) =>
path.reduce((obj, segment) => setDefault(obj, segment, {}), obj);
// EXAMPLES
let temp = {};
// returns the set value, similar to assignment
console.log('temp.a.b.c.d:',
setDeep(temp, ['a', 'b', 'c', 'd'], 'one'))
// not destructive to 'one'
setDeep(temp, ['a', 'b', 'z'], 'two')
// does not overwrite, returns previously set value
console.log('temp.a.b.z: ',
setDefaultDeep(temp, ['a', 'b', 'z'], 'unused'))
// creates new, returns current value
console.log('temp["a.1"]: ',
setDefault(temp, 'a.1', 'three'))
// can also be used as a getter
console.log("temp.x.y.z: ",
setStructure(temp, ['x', 'y', 'z']))
console.log("final object:", temp)
I'm not sure why anyone would want string paths:
They are ambiguous for keys with periods
You have to build the strings in the first place
Since I started with something from this page, I wanted to contribute back
Other examples overwrote the final node even if it was set, and that wasn't what I wanted.
Also, if returnObj is set to true, it returns the base object. By default, falsy, it returns the deepest node.
function param(obj, path, value, returnObj) {
if (typeof path == 'string') path = path.split(".");
var child = obj;
path.forEach((key, i) => {
if (!(key in child)) {
child[key] = (i < path.length-1) ? {} : value || {};
}
child = child[key];
});
return returnObj ? obj : child;
}
var x = {};
var xOut = param(x, "y.z", "setting")
console.log(xOut);
xOut = param(x, "y.z", "overwrite") // won't set
console.log(xOut);
xOut = param(x, "y.a", "setting2")
console.log(xOut);
xOut = param(x, "y.a", "setting2", true) // get object rather than deepest node.
console.log(xOut);
You can also do something where numeric keys are placed in arrays (if they don't already exist). Note that numeric keys won't convert to arrays for the first element of the path, since that's set by the type of your base-object.
function isNumber(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
function param(obj, path, value, returnObj) {
if (typeof path == 'string') path = path.split(".");
var child = obj;
path.forEach((key, i) => {
var nextKey = path[i+1];
if (!(key in child)) {
child[key] = (nextKey == undefined && value != undefined
? value
: isNumber(nextKey)
? []
: {});
}
child = child[key];
});
return returnObj ? obj : child;
}
var x = {};
var xOut = param(x, "y.z", "setting")
console.log(xOut);
xOut = param(x, "y.z", "overwrite") // won't set
console.log(xOut);
xOut = param(x, "y.a", "setting2")
console.log(xOut);
xOut = param(x, "y.a", "setting2", true) // get object rather than deepest node.
xOut = param(x, "1.0.2.a", "setting")
xOut = param(x, "1.0.1.a", "try to override") // won't set
xOut = param(x, "1.0.5.a", "new-setting", true) // get object rather than deepest node.
console.log(xOut);
Naturally, when the numeric keys are greater than 0, you might see some undefined gaps.
Practical uses of this might be
function AddNote(book, page, line) {
// assume a global global notes collection
var myNotes = param(allNotes, [book, page, line], []);
myNotes.push('This was a great twist!')
return myNotes;
}
var allNotes = {}
var youthfulHopes = AddNote('A Game of Thrones', 4, 2, "I'm already hooked, at least I won't have to wait long for the books to come out!");
console.log(allNotes)
// {"A Game of Thrones": [undefined, undefined, undefined, undefined, [undefined, undefined, ["I'm already hooked, at least I won't have to wait long for the books to come out!"]]]}
console.log(youthfulHopes)
// ["I'm already hooked, at least I won't have to wait long for the books to come out!"]
function initPath(obj, path) {
path.split('.').reduce((o, key) => (
Object.assign(o, {[key]: Object(o[key])}),
o[key]
), obj);
return obj;
}
Usage
const obj = { a: { b: 'value1' } };
initPath(obj, 'a.c.d').a.c.d='value2';
/*
{
"a": {
"b": "value1",
"c": {
"d": "value2"
}
}
}
*/
simple answer. on es6, im using this
const assign = (obj, path, value) => {
let keyPath = path.split('.')
let lastKeyIndex = keyPath.length - 1
for (let i = 0; i < lastKeyIndex; ++i) {
let key = keyPath[i]
if (!(key in obj)) {
obj[key] = {}
}
obj = obj[key]
}
obj[keyPath[lastKeyIndex]] = value
}
example json
const obj = {
b: 'hello'
}
you can add new key
assign(obj, 'c.d.e', 'this value')
and you get like bellow
console.log(obj)
//response example
obj = {
b: 'hello',
c: {
d: {
e: 'this value'
}
}
}
function createObj(keys, value) {
let obj = {}
let schema = obj
keys = keys.split('.')
for (let i = 0; i < keys.length - 1; i++) {
schema[keys[i]] = {}
schema = schema[keys[i]]
}
schema[keys.pop()] = value
return obj
}
let keys = 'value1.value2.value3'
let value = 'Hello'
let obj = createObj(keys, value)
Eval is probably overkill but the result is simple to visualize, with no nested loops or recursion.
function buildDir(obj, path){
var paths = path.split('_');
var final = paths.pop();
for (let i = 1; i <= paths.length; i++) {
var key = "obj['" + paths.slice(0, i).join("']['") + "']"
console.log(key)
eval(`${key} = {}`)
}
eval(`${key} = '${final}'`)
return obj
}
var newSettingName = "Modules_Video_Plugin_JWPlayer";
var Settings = buildDir( {}, newSettingName );
Basically you are progressively writing a string "obj['one']= {}", "obj['one']['two']"= {} and evaling it;

Compare array with object key in javascript

I have below array ,
["aa","bb"]
I have below object,
{"aa":1,"bb":2,"cc":3}
and I need result below,
{"cc":3}
I need to compare array with object keys, using looping it is possible,but it take more time for large amount data, is there any other fastest way to get my result without using loop, Anyhelp Appreciated...
To avoid quadratic time complexity you could use a Set and pass that as the this argument to the filter. Then recompose an object from those filtered keys with Object.assign:
var a = ["aa","bb"];
var b = {"aa":1,"bb":2,"cc":3};
var difference = Object.assign(...Object.keys(b).filter(function(e){
return !this.has(e);
}, new Set(a)).map(key => ({ [key]: b[key] }) ));
console.log(difference);
If you are OK with mutating the object, then you can use delete to remove the unwanted properties.
const props = ["aa","bb"];
const obj = {"aa": 1,"bb": 2,"cc": 3};
props.forEach(prop => delete obj[prop]);
console.log(obj);
Or just loop over the array of property names with for...of.
const props = ["aa","bb"];
const obj = {"aa": 1,"bb": 2,"cc": 3};
for (prop of props) delete obj[prop];
console.log(obj);
If you don't want to mutate the object, but instead create a new one without he unwanted properties, then you can do a shallow clone first with
newObj = {...obj};
Loop through the object and check to see if the property doesn't exist in the array:
var a = ["aa","bb"];
var o = {"aa":1,"bb":2,"cc":3};
// Loop through object
for(var prop in o){
// If property doesn't exist in array...
if(a.indexOf(prop) === -1){
console.log(prop + ":" + o[prop]);
}
}
Something like this maybe?
var a = ["aa","bb"];
var b = {"aa":1,"bb":2,"cc":3};
var result = {};
Object.keys(b).filter(function(e){
return a.indexOf(e) < 0;
}).forEach(function(e){
result[e] = b[e];
});
console.log(result);
you can loop through the object and compare the object keys with the array items and return the missing keys as an object.
var a = ["aa","bb"] ;
var o = {"aa":1,"bb":2,"cc":3,'dd':4};
function get(o){
var p = {};
for(var key in o){
if(a.indexOf(key) == -1){
p[key] = o[key];
}
}
return p;
}
var c = get(o);
console.log(c);
You can use Object.assign(), Object.entries(), Array.prototype.includes() to determine if current key in object is an element of array, return object using computed property or false, which does not result in property being assigned to new object
var arr = ["aa","bb"]
var obj = {"aa":1,"bb":2,"cc":3}
var res = Object.assign({}
, ...Object.entries(obj).map(([key, prop]) =>
!arr.includes(key) && {[key]:prop})
);
console.log(res);

JavaScript--merge two array objects into one arr object then loop through each index/object

var arrObj1 = [{a:1},{b:2}, {c:3}]
var arrObj2 = [{operator: LESS THAN}, {operator:GREATER THAN}, {operator:"NOT EQUAL"}]
so I want to merge obj at arrObj2[0] into obj at arrObj1[0], arrObj2[1] into obj at arrObj1[1] and so forth...
//resultArrObj = [{a:1, operator: LESS THAN}, {b:2, operator:"GREATER THAN}];
After (or while merging) i need to loop through each object and return a new obj which will have the properties(field, input, operator) and push into array.
field = key from arrObj1 (a, b, c)
input = value from arrObj1(1,2,3)
operator = "operator" from arrObj 2.
// var endResult = [{field:a, input:1, operator: LESS THAN}, {field:b, input:2, operator:GREATER THAN}];
thanks!!
I believe this does what you are looking for. It assumes that you know your data and doesn't do any error checking but just combines them the way that you asked.
var arrObj1 = [{a:1},{b:2}, {c:3}];
var arrObj2 = [{operator: "LESS THAN"}, {operator:"GREATER THAN"}, {operator:"NOT EQUAL"}];
var endResult = [];
var counter = 0;
var objNew = {};
arrObj1.forEach(function(fieldInput){
for ( field in fieldInput ){
objNew.field=field;
objNew.input=fieldInput[field];
objNew.operator=arrObj2[counter]['operator'];
counter++;
endResult.push(objNew);
objNew={};
};
})
// var endResult = [{field:a, input:1, operator: LESS THAN}, {field:b, input:2, operator:GREATER THAN}, {field:c, input:3, operator:NOT EQUAL}]
Let's break this problem into 2 steps:
1) Merging objects
2) Processing objects
We can write a generalized merging function:
if (!Object.prototype.extend) {
Object.prototype.extend = function (object) {
for (key in object) {
if (typeof object[key] === 'object' &&
typeof this[key] === 'object' &&
this.hasOwnProperty(key)) {
this[key].extend(object[key]);
} else {
this[key] = object[key];
}
}
return this;
};
};
The code above creates a method that all Objects share. It works like this:
var obj1 = { 'a': 7 },
obj2 = { 'b': 2 };
obj1.extend(obj2);
console.log(obj1); // { 'a': 7, 'b': 2 };
I usually wouldn't modify Objects like I have done with the .extend method, but it should be trivial to build something that does the same thing but has a different API. (for example, function extend (obj1, obj2) { do stuff.. and return extended object }
Anyways. That should solve the first part about merging objects.
The second part, processing this merged object, is simply using a for loop that iterates over the merged object, and pushes new objects into an array. It would look something like this:
for (var prop in mergedObject) {
var tempObject = {};
if (prop === 'operator') {
tempObject.operator = mergedObject.operator;
} else {
tempObject.field = prop;
tempObject.input = tempObject[prop];
}
endResult.push(tempObject);
}
Hopefully this helps you.

retrieving the value of object using strings

Here I am in a situation where I have to work only with strings, and because of this I also have to retrieve the value of an object from strings, in short:
to retrieve the value from an object we write:
someObject.property1.name // for say
but in my case i want to retrieve value from an object using string, i.e
'someObject.property1.name' // for say
since I was not so confident that I could do this, so I preferred tho search on internet and the most suitable solution which I got was
#1
Object.byString = function(o, s) {
s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
s = s.replace(/^\./, ''); // strip a leading dot
var a = s.split('.');
while (a.length) {
var n = a.shift();
if (n in o) {
o = o[n];
} else {
return;
}
}
return o;
}
from here
#2
var deep_value = function(obj, path){
for (var i=0, path=path.split('.'), len=path.length; i<len; i++){
obj = obj[path[i]];
};
return obj;
};
from here
but as I said they are the most suitable example because they all are taking one extra parameter i.e. obj, O and so on... which is creating trouble for me, so I tried to improve the above code in search 2 because it is compact, and that results in failure. That code is:
var obj = {
foo: { bar: 'baz' }
};
var deep_value = function(path){
var obj = path.split('.');
obj = obj[0];
for (var i=0, path=path.split('.'), len=path.length; i<len; i++){
obj = obj[path[i+1]];
};
return obj;
};
alert(deep_value('obj.foo.bar'));
(I edited in his code for just an experiment). the above code does not need obj which is a perfect code - if it worked - and don't see any mistake, then why this code is not working, what is the correct code?
JSFIDDLE
thanks in advance
There were a couple problems with your #3 option:
First obj = obj[0]; was just going to have obj === "obj" which isn't going to help you at all. You need to actually get window["obj"] to get the top level object.
Second, you were traversing the for loop one too many times and going off the end of the path array.
Making changes in both these areas will make it work if obj is at the top level scope:
var obj = {
foo: { bar: 'baz' }
};
var deep_value = function(path, baseObj){
baseObj = baseObj || window;
var obj = path.split('.');
obj = baseObj[obj[0]];
for (var i=1, path=path.split('.'), len=path.length; i<len; i++){
obj = obj[path[i]];
};
return obj;
};
alert(deep_value('obj.foo.bar'));
Working demo: http://jsfiddle.net/jfriend00/jGb5p/
Here's a bit of a cleaned-up version:
var obj = {
foo: { bar: 'baz' }
};
var deep_value = function(path, baseObj){
baseObj = baseObj || window;
var pieces = path.split('.');
// get root level object
var obj = baseObj[pieces[0]];
for (var i = 1, len = pieces.length; i < len; i++){
obj = obj[pieces[i]];
}
return obj;
};
console.log(deep_value('obj.foo.bar'));
Working demo: http://jsfiddle.net/jfriend00/7J4Jb/
This be expressed compactly using reduce:
function eval_dotted_path(path) {
return path.split('.').reduce(function(value, segment) {
return value && value[segment];
}, window);
}
This splits up the path into "segments" on the dots, then calls reduce to "loop" over the segments, finding the next value inside the object on each iteration. The first segment is found in the global namespace, aka "window".
The value && value[segment] ensures that if at any point the current value is null/undefined (because the segment property was not present), undefined/null is returned.
If instead you want to find the value indicated by the dotted path starting from a known object, you can tweak this as
function eval_dotted_path_from_object(object, path) {
return path.split('.').reduce(function(value, segment) {
return value && value[segment];
}, object);
}
after which you can redefine the initial function as
function eval_dotted_path(path) {
return eval_dotted_path_from_object(window, path);
}
If you're in an old environment without Array.prototype.reduce, consider using Underscore's _.reduce, or a polyfill; some frameworks may provide the polyfill for you.

How to remove the java script variable refrences

var depe = {}
var array1 = [];
array1.push('value1');
var array2 = [];
array2.push('value1a');
array2.push('value2');
depe['key1'] = array1;
depe['key2'] = array2; // now variable depe contains {"key1":["value1"],"key2":["value1a","value2"]}
var temp = depe['key2']; // now variable temp contains ["value1a","value1a"]
temp[temp.length] = 'newValue' // now variable temp contains ["value1a","value1a","newValue"]
After execution of last line variable depe contains
{"key1":["value1"],"key2":["value1a","value2","newValue"]}
I don't want to be updated in variable depe. How do I resolve it ?
By default, arrays are passed around by reference so if you assign var temp = depe['key2']; and then add another item to temp it is changing the one and only one array so it will show in both places.
To make a separate shallow copy of an array that you can change, you have to make an explicit copy which you can do with array.slice(0):
// get a copy of the array in depe['key2']
// that I can change independently of what is in depe
var temp = depe['key2'].slice(0);
temp[temp.length] = 'newValue';
You have to clone objects in javascript.
Please read: http://jsperf.com/cloning-an-object/2
or
function clone(obj) {
if (null == obj || "object" != typeof obj) return obj;
var copy = obj.constructor();
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
}
return copy;
}
from: How do I correctly clone a JavaScript object?
you need to clone your array
function cloneArray(array){
var newArray = [];
for(var i = 0; i < array.length; i++){
newArray[i] = cloneValue(array[i]);
}
return newArray;
}
to clone a value
function cloneValue(value){
if (typeof value != 'object')
return value;
else {
var newObj = {};
for (var prop in value){
newObj[prop] = value[prop];
}
return newObj;
}
}
and use it like this
var arr2 = cloneArray(arr1);
i think u should use slice() method to get a clone of depe array. you can read this for your reference : Copying array by value in JavaScript
OR u can make this prototype function:
Array.prototype.clone = function() {
return this.slice(0);
};
so in line : var temp = depe['key2'];
u can use depe['key2'].clone() instead of depe['key2'].slice(0)
Hope this help you.

Categories