i have two objects, a master and a temp
the master looks like
{
"gnome":{
"child":{
name:"child",
race:"gnome"
},
"youngling":{
name:"youngling",
race:"gnome"
}
},
"human":{...},
...
}
and the temp looks like
{
"gnome":{
"man":{
name:"man",
race:"gnome"
}
}
what i am trying to do is have the temp be added to the master like
{
"gnome":{
"child":{...},
"youngling":{...},
"man":{...}
},
"human":{...},
...
}
what i currently have
let obj = {}
function generateJson() {
let race = getinput("race").value
let name = getinput("name").value
let temp = {}
temp[`${race}`] = {}
temp[`${race}`][`${name}`] = {
name: name,
race: race
}
obj = Object.assign(obj, temp)
}
all it does is empties and override the first duplicate key with the temp value
a.e. {gnome:{man:{..}}}
earlier this question was closed because it should have been awnsered with How can I merge properties of two JavaScript objects dynamically?
which sadly it didn't, all of the solutions override objects within objects, i want to add to similar keys
Based on your example, this should work
<script>
function merge_without_override(master, temp) {
for(var key in temp) {
if( !temp.hasOwnProperty(key) )
continue;
if(master[key] !== undefined) // key already exists in master.
continue;
master[key] = Object.assign(temp[key]); // key doesnt exist, assign it.
}
}
var master = {
"gnome":{
"child":{
name:"child",
race:"gnome"
},
"youngling":{
name:"youngling",
race:"gnome"
}
},
"human":{}
};
var temp = {
"gnome":{
"man":{
name:"man",
race:"gnome"
}
}
};
console.log("master before merge");
console.log(master);
merge_without_override(master["gnome"], temp["gnome"]);
console.log("master after merge");
console.log(master);
</script>
Output (in jsfiddle):
{
gnome: {
child: { ... },
man: { ... },
youngling: { ... }
},
human: { ... }
}
JsFiddle: https://jsfiddle.net/h10fpcx2/
Chris Ferdinandi wrote a helper for this here;
I've added an updated version below, but wanted to add a fix for my biggest frustration with Object.assign.
It mutates the source material. To fix this, you can add an empty object as the first argument of the deepAssign function, or use function copyDeepAssign below.
// mutates the source material
function deepAssign(...args) {
// Make sure there are objects to merge
const len = args.length;
if (len < 1) return;
const main = args[0];
if (len < 2) return main
// Merge all objects into first
let i = 0,
curr;
while (i < len) {
curr = args[i];
for (var key in curr) {
// If it's an object, recursively merge
// Otherwise, push to key
if (Object.prototype.toString.call(curr[key]) === '[object Object]') {
main[key] = deepAssign(main[key] || {}, curr[key]);
} else {
main[key] = curr[key];
}
}
i++;
}
return main;
}
// Doesn't mutate the source material
function copyDeepAssign(...args) {
const base = {};
return deepAssign(base, ...args);
}
I have the following function which takes some values that the user has entered and creates JavaScript objects from those values, then puts those objects in an array and returns the array:
function createObjects() {
var objectArray = [];
for (var i = 0; i < someCount; i++) {
var object = {
property1: someProperty1,
property2: someProperty2,
property3: someProperty3,
property4: someProperty4
};
objectArray.push(object);
}
return objectArray;
}
Now, I want to compare these objects' properties and determine whether any two contain all of the same values for property1, property2, property3, and property4. If any two of these objects have all four of the same values for these properties, I want a validation check to return false. Here is what I have so far:
function objectsAreUnique() {
var objects = createObjects();
for(var i = 0; i < objects.length; i++) {
//need to determine whether all four of the properties are the same for any two objects
//if(objectsAreSame) { return false; }
}
return true;
}
I have a few ideas, but I'm interested to see what is the most efficient way to achieve this. Thanks!
If you can guarantee that the properties will always be inserted in the same order (which will be the case if using an object literal as in your example), you can do this in ~O(n) using JSON.stringify and a Set:
function objectsAreUnique() {
const objects = createObjects();
return (new Set(objects.map(o => JSON.stringify(o)))).size == objects.length;
}
First, create a function to test whether two objects are the same. You can enter each property individually, or get creative with the JSON.stringify function
properties individually:
function objectsIdentical(obj1, obj2) {
return obj1.property1 == obj2.property1 && obj1.property2 == obj2.property2 && obj1.property3 == obj2.property3 && obj1.property4 == obj2.property4;
}
JSON.stringify (recommended for objects with many properties)
function objectsIdentical(obj1, obj2) {
return JSON.stringify(obj1).replace(/^.|.$/g, "").split(",").sort().join(",") == JSON.stringify(obj2).replace(/^.|.$/g, "").split(",").sort().join(",");
}
Then, you can use a for loop to check if any of them are identical.
for (let i=0; i<objects.length-1; i++) {
for (let j=i+1; j<objects.length; j++) {
if (objectsIdentical(objects[i], objects[j])) {
return false;
}
}
}
return true;
If you're familiar with the "some" function, you can use that.
return !objects.some((v, i) => objects.slice(i+1).some(w => objectsIdentical(v, w)))
function objectsAreUnique() {
var objects = createObjects();
var stringifiedAndSorted = objects.map(obj => JSON.stringify(obj)).sort()
for(var i = 0; i < stringifiedAndSorted.length-1; i++) {
if(i === i+1)
return false;
}
return true;
}
I am using node, mocha, and chai for my application. I want to test that my returned results data property is the same "type of object" as one of my model objects (Very similar to chai's instance). I just want to confirm that the two objects have the same sets of property names. I am specifically not interested in the actual values of the properties.
Let's say I have the model Person like below. I want to check that my results.data has all the same properties as the expected model does. So in this case, Person which has a firstName and lastName.
So if results.data.lastName and results.data.firstName both exist, then it should return true. If either one doesn't exist, it should return false. A bonus would be if results.data has any additional properties like results.data.surname, then it would return false because surname doesn't exist in Person.
This model
function Person(data) {
var self = this;
self.firstName = "unknown";
self.lastName = "unknown";
if (typeof data != "undefined") {
self.firstName = data.firstName;
self.lastName = data.lastName;
}
}
You can serialize simple data to check for equality:
data1 = {firstName: 'John', lastName: 'Smith'};
data2 = {firstName: 'Jane', lastName: 'Smith'};
JSON.stringify(data1) === JSON.stringify(data2)
This will give you something like
'{firstName:"John",lastName:"Smith"}' === '{firstName:"Jane",lastName:"Smith"}'
As a function...
function compare(a, b) {
return JSON.stringify(a) === JSON.stringify(b);
}
compare(data1, data2);
EDIT
If you're using chai like you say, check out http://chaijs.com/api/bdd/#equal-section
EDIT 2
If you just want to check keys...
function compareKeys(a, b) {
var aKeys = Object.keys(a).sort();
var bKeys = Object.keys(b).sort();
return JSON.stringify(aKeys) === JSON.stringify(bKeys);
}
should do it.
2 Here a short ES6 variadic version:
function objectsHaveSameKeys(...objects) {
const allKeys = objects.reduce((keys, object) => keys.concat(Object.keys(object)), []);
const union = new Set(allKeys);
return objects.every(object => union.size === Object.keys(object).length);
}
A little performance test (MacBook Pro - 2,8 GHz Intel Core i7, Node 5.5.0):
var x = {};
var y = {};
for (var i = 0; i < 5000000; ++i) {
x[i] = i;
y[i] = i;
}
Results:
objectsHaveSameKeys(x, y) // took 4996 milliseconds
compareKeys(x, y) // took 14880 milliseconds
hasSameProps(x,y) // after 10 minutes I stopped execution
If you want to check if both objects have the same properties name, you can do this:
function hasSameProps( obj1, obj2 ) {
return Object.keys( obj1 ).every( function( prop ) {
return obj2.hasOwnProperty( prop );
});
}
var obj1 = { prop1: 'hello', prop2: 'world', prop3: [1,2,3,4,5] },
obj2 = { prop1: 'hello', prop2: 'world', prop3: [1,2,3,4,5] };
console.log(hasSameProps(obj1, obj2));
In this way you are sure to check only iterable and accessible properties of both the objects.
EDIT - 2013.04.26:
The previous function can be rewritten in the following way:
function hasSameProps( obj1, obj2 ) {
var obj1Props = Object.keys( obj1 ),
obj2Props = Object.keys( obj2 );
if ( obj1Props.length == obj2Props.length ) {
return obj1Props.every( function( prop ) {
return obj2Props.indexOf( prop ) >= 0;
});
}
return false;
}
In this way we check that both the objects have the same number of properties (otherwise the objects haven't the same properties, and we must return a logical false) then, if the number matches, we go to check if they have the same properties.
Bonus
A possible enhancement could be to introduce also a type checking to enforce the match on every property.
If you want deep validation like #speculees, here's an answer using deep-keys (disclosure: I'm sort of a maintainer of this small package)
// obj1 should have all of obj2's properties
var deepKeys = require('deep-keys');
var _ = require('underscore');
assert(0 === _.difference(deepKeys(obj2), deepKeys(obj1)).length);
// obj1 should have exactly obj2's properties
var deepKeys = require('deep-keys');
var _ = require('lodash');
assert(0 === _.xor(deepKeys(obj2), deepKeys(obj1)).length);
or with chai:
var expect = require('chai').expect;
var deepKeys = require('deep-keys');
// obj1 should have all of obj2's properties
expect(deepKeys(obj1)).to.include.members(deepKeys(obj2));
// obj1 should have exactly obj2's properties
expect(deepKeys(obj1)).to.have.members(deepKeys(obj2));
Here's a deep-check version of the function provided above by schirrmacher.
Below is my attempt. Please note:
Solution does not check for null and is not bullet proof
I haven't performance tested it. Maybe schirrmacher or OP can do that and share for the community.
I'm not a JS expert :).
function objectsHaveSameKeys(...objects) {
const allKeys = objects.reduce((keys, object) => keys.concat(Object.keys(object)), [])
const union = new Set(allKeys)
if (union.size === 0) return true
if (!objects.every((object) => union.size === Object.keys(object).length)) return false
for (let key of union.keys()) {
let res = objects.map((o) => (typeof o[key] === 'object' ? o[key] : {}))
if (!objectsHaveSameKeys(...res)) return false
}
return true
}
Update 1
A 90% improvement on the recursive deep-check version is achieved on my computer by skipping the concat() and adding the keys directly to the Set(). The same optimization to the original single level version by schirrmacher also achieves ~40% improvement.
The optimized deep-check is now very similar in performance to the optimized single level version!
function objectsHaveSameKeysOptimized(...objects) {
let union = new Set();
union = objects.reduce((keys, object) => keys.add(Object.keys(object)), union);
if (union.size === 0) return true
if (!objects.every((object) => union.size === Object.keys(object).length)) return false
for (let key of union.keys()) {
let res = objects.map((o) => (typeof o[key] === 'object' ? o[key] : {}))
if (!objectsHaveSameKeys(...res)) return false
}
return true
}
Performance Comparison
var x = {}
var y = {}
var a = {}
for (var j = 0; j < 10; ++j){
a[j] = j
}
for (var i = 0; i < 500000; ++i) {
x[i] = JSON.parse(JSON.stringify(a))
y[i] = JSON.parse(JSON.stringify(a))
}
let startTs = new Date()
let result = objectsHaveSameKeys(x, y)
let endTs = new Date()
console.log('objectsHaveSameKeys = ' + (endTs - startTs)/1000)
Results
A: Recursive/deep-check versions*
objectsHaveSameKeys = 5.185
objectsHaveSameKeysOptimized = 0.415
B: Original non-deep versions
objectsHaveSameKeysOriginalNonDeep = 0.517
objectsHaveSameKeysOriginalNonDeepOptimized = 0.342
function getObjectProperties(object, propertiesString = '') {
let auxPropertiesString = propertiesString;
for (const objectLevel of Object.keys(object).sort((a, b) => a.localeCompare(b))) {
if (typeof object[objectLevel] === 'object') {
auxPropertiesString += getObjectProperties(object[objectLevel], auxPropertiesString);
} else {
auxPropertiesString += objectLevel;
}
}
return auxPropertiesString;
}
function objectsHaveTheSameKeys(objects) {
const properties = [];
for (const object of objects) {
properties.push(getObjectProperties(object));
}
return properties.every(eachProperty => eachProperty === properties[0]);
}
It's a bit rudimentary, but should do the work in case you want to compare properties.
Legacy Browser Object Compare Function
Unlike the other solutions posted here, my Object Compare Function works in ALL BROWSERS, modern or legacy, including very old browsers, even Internet Explorer 5 (c.2000)!
Features:
Can compare an unlimited list of Objects. All must match or fails!
Ignores property order
Only compares "own" properties (i.e. non-prototype)
Matches BOTH property names and property values (key-value pairs)!
Matches functions signatures in objects!
Every object submitted is cross-compared with each other to detect missing properties in cases where one is missing but not in the other
Avoids null, undefined, NaN, Arrays, non-Objects, etc.
{} empty object detection
Works in almost ALL BROWSERS, including even Internet Explorer 5 and many other legacy browsers!
Note the function does not detect complex objects in properties, but you could rewrite the function to call them recursively.
Just call the method with as many objects as you like!
ObjectCompare(myObject1,myObject2,myObject3)
function ObjectCompare() {
try {
if (arguments && arguments.length > 0) {
var len = arguments.length;
if (len > 1) {
var array = [];
for (var i = 0; i < len; i++) {
if (
((typeof arguments[i] !== 'undefined') || (typeof arguments[i] === 'undefined' && arguments[i] !== undefined))
&& (arguments[i] !== null)
&& !(arguments[i] instanceof Array)
&& ((typeof arguments[i] === 'object') || (arguments[i] instanceof Object))
) {
array.push(arguments[i]);
}
}
if (array.length > 1) {
var a1 = array.slice();
var a2 = array.slice();
var len1 = a1.length;
var len2 = a2.length;
var noKeys = true;
var allKeysMatch = true;
for (var x = 0; x < len1; x++) {
console.log('---------- Start Object Check ---------');
//if (len2>0) {
// a2.shift();// remove next item
//}
len2 = a2.length;
if (len2 > 0 && allKeysMatch) {
for (var y = 0; y < len2; y++) {
if (x !== y) {// ignore objects checking themselves
//console.log('Object1: ' + JSON.stringify(a1[x]));
//console.log('Object2: ' + JSON.stringify(a2[y]));
console.log('Object1: ' + a1[x].toString());
console.log('Object2: ' + a2[y].toString());
var ownKeyCount1 = 0;
for (var key1 in a1[x]) {
if (a1[x].hasOwnProperty(key1)) {
// ---------- valid property to check ----------
ownKeyCount1++;
noKeys = false;
allKeysMatch = false;// prove all keys match!
var ownKeyCount2 = 0;
for (var key2 in a2[y]) {
if (a2[y].hasOwnProperty(key2) && !allKeysMatch) {
ownKeyCount2++;
if (key1 !== key1 && key2 !== key2) {// NaN check
allKeysMatch = true;// proven
break;
} else if (key1 === key2) {
if (a1[x][key1].toString() === a2[y][key2].toString()) {
allKeysMatch = true;// proven
console.log('KeyValueMatch=true : ' + key1 + ':' + a1[x][key1] + ' | ' + key2 + ':' + a2[y][key2]);
break;
}
}
}
}
if (ownKeyCount2 === 0) {// if second objects has no keys end early
console.log('-------------- End Check -------------');
return false;
}
// ---------------------------------------------
}
}
console.log('-------------- End Check -------------');
}
}
}
}
console.log('---------------------------------------');
if (noKeys || allKeysMatch) {
// If no keys in any objects, assume all objects are {} empty and so the same.
// If all keys match without errors, then all object match.
return true;
} else {
return false;
}
}
}
console.log('---------------------------------------');
return true;// one object
}
console.log('---------------------------------------');
return false;// no objects
} catch (e) {
if (typeof console !== 'undefined' && console.error) {
console.error('ERROR : Function ObjectCompare() : ' + e);
} else if (typeof console !== 'undefined' && console.warn) {
console.warn('WARNING : Function ObjectCompare() : ' + e);
} else if (typeof console !== 'undefined' && console.log) {
console.log('ERROR : Function ObjectCompare() : ' + e);
}
return false;
}
}
// TESTING...
var myObject1 = new Object({test: 1, item: 'hello', name: 'john', f: function(){var x=1;}});
var myObject2 = new Object({item: 'hello', name: 'john', test: 1, f: function(){var x=1;}});
var myObject3 = new Object({name: 'john', test: 1, item: 'hello', f: function(){var x=1;}});
// RETURNS TRUE
//console.log('DO ALL OBJECTS MATCH? ' + ObjectCompare(myObject1, myObject2, myObject3));
If you are using underscoreJs then you can simply use _.isEqual function
and it compares all keys and values at each and every level of hierarchy like below example.
var object = {"status":"inserted","id":"5799acb792b0525e05ba074c","data":{"workout":[{"set":[{"setNo":1,"exercises":[{"name":"hjkh","type":"Reps","category":"Cardio","set":{"reps":5}}],"isLastSet":false,"index":0,"isStart":true,"startDuration":1469689001989,"isEnd":true,"endDuration":1469689003323,"speed":"00:00:01"}],"setType":"Set","isSuper":false,"index":0}],"time":"2016-07-28T06:56:52.800Z"}};
var object1 = {"status":"inserted","id":"5799acb792b0525e05ba074c","data":{"workout":[{"set":[{"setNo":1,"exercises":[{"name":"hjkh","type":"Reps","category":"Cardio","set":{"reps":5}}],"isLastSet":false,"index":0,"isStart":true,"startDuration":1469689001989,"isEnd":true,"endDuration":1469689003323,"speed":"00:00:01"}],"setType":"Set","isSuper":false,"index":0}],"time":"2016-07-28T06:56:52.800Z"}};
console.log(_.isEqual(object, object1));//return true
If all the keys and values for those keys are same in both the objects then it will return true, otherwise return false.
Here is my attempt at validating JSON properties. I used #casey-foster 's approach, but added recursion for deeper validation. The third parameter in function is optional and only used for testing.
//compare json2 to json1
function isValidJson(json1, json2, showInConsole) {
if (!showInConsole)
showInConsole = false;
var aKeys = Object.keys(json1).sort();
var bKeys = Object.keys(json2).sort();
for (var i = 0; i < aKeys.length; i++) {
if (showInConsole)
console.log("---------" + JSON.stringify(aKeys[i]) + " " + JSON.stringify(bKeys[i]))
if (JSON.stringify(aKeys[i]) === JSON.stringify(bKeys[i])) {
if (typeof json1[aKeys[i]] === 'object'){ // contains another obj
if (showInConsole)
console.log("Entering " + JSON.stringify(aKeys[i]))
if (!isValidJson(json1[aKeys[i]], json2[bKeys[i]], showInConsole))
return false; // if recursive validation fails
if (showInConsole)
console.log("Leaving " + JSON.stringify(aKeys[i]))
}
} else {
console.warn("validation failed at " + aKeys[i]);
return false; // if attribute names dont mactch
}
}
return true;
}
I am currently doing this by this method. Need a better implementation for this Scenario:
Here is the following:
var testjson = {
"key1":"val1",
"key2":"val2",
"key3":{
"k2":"v2",
"k3":{
"k4":"v4",
"k5":"v5"
}
},
"haskey": function (base, path) {
var current = base;
var components = path.split(".");
for (var i = 0; i < components.length; i++) {
if ((typeof current !== "object") || (!current.hasOwnProperty(components[i]))) {
return false;
}
current = current[components[i]];
}
return true;
}
}
console.log( testjson.haskey(testjson,"key3.k3.k4"));
First of all Sorry for asking misleading question.I just need a method for checking a nested object Property exists in a JavaScript object or not. There is nothing to do with prototype with this question. I just created a custom method with two argument one for object and another for property to check.This works fine, I came to a conclusion after looking at this ans->Checking if a key exists in a JavaScript object?
Here is my Method to check the property exist or not in an Object
var testobject = {
"key1": "value",
"key2": {
"key3": "value"
}
}
function checkproperty(object, path) {
var current = object;
var components = path.split(".");
for (var i = 0; i < components.length; i++) {
if ((typeof current !== "object") || (!current.hasOwnProperty(components[i]))) {
return false;
}
current = current[components[i]];
}
return true;
}
console.log(checkproperty(testobject, "key2.key3"))
Say i have this function that dynamically creates my namespace for me when I just pass it a string, (I'm pretty sure basically what YUI JS library does):
MyObj.namespace('fn.method.name');
would result in
MyObj.fn.method.name = {}
being created - all three levels being equivalent to an empty object.
Now, what I want to do, though, is make the last level, in this case name, set to a function, but without having to redeclare the newly created object.
So instead of doing this:
function fnName() { /* some code here */ }
MyObj.namespace('fn.method.name');
MyObj.fn.method.name = new fnName();
i want to call something like:
MyObj.add('fn.method.name', fnName);
And internally, the add method would programmatically instantiate the passed in function:
MyObj.fn.method.name = new fnName()
In the way I have it implemented, I can create the namespace object and set it to an empty object, however, when I try to instantiate a passed in function and associate that namespace with the passed in function, it never gets added to the namespace. Instead, an empty object is always returned. Any ideas?
edit: Here is the namespace method. this is attached to the base object as a JSON object, so please ignore the formatting:
namespace: function (ns) {
var _ns = ns.split('.'),
i = 0, nsLen = _ns.length,
root = this;
if (_ns[0] === gNS) {
_ns.shift();
nsLen = _ns.length;
}
for (i = 0; i < nsLen; i++) {
// create a property if it doesn't exist
var newNs = _ns[i];
if (typeof root[newNs] === "undefined") {
root[newNs] = {};
}
root = root[newNs];
}
return root;
}
edit2 - removed the passed in fn argument
Were you looking for something like this:
var root = {};
function create(ns, fn) {
var nsArray = ns.split(/\./);
var currentNode = root;
while(nsArray.length > 1) {
var newNS = nsArray.shift();
if(typeof currentNode[newNS] === "undefined") {
currentNode[newNS] = {};
}
currentNode = currentNode[newNS];
}
if(fn) {
currentNode[nsArray.shift()] = fn;
}
else {
currentNode[nsArray.shift()] = {};
}
}
Then:
create("a.b.c");
console.log(root.a);
console.log(root.a.b);
console.log(root.a.b.c);
Gives:
Object { b={...}}
Object { c={...}}
Object {}
And:
create("d.e.f", function() { console.log("o hai"); });
console.log(root.d);
console.log(root.d.e);
console.log(root.d.e.f);
Gives:
Object { e={...}}
Object {}
function()
Calling the function you defined:
root.d.e.f();
Gives:
o hai
Well you haven't given the namespace function but your add function could look something like this:
MyObj.add = function (namespace, value) {
var names = namespace.split('.'), current = this, name;
while (names.length > 1) {
name = names.shift();
current[name] = {};
current = current[name];
}
current[names[0]] = value;
};
This code assigns the value given to the last part of the namespace. You could modify it to current[names[0] = new value(); if you want the object constructed by the passed in function (and you are assuming the constructor function takes no arguments).
function ns() {
var root = window;
for (var i = 0; i < arguments.length; i++) {
var arr = arguments[i].split(/\./);
for (var j = 0; j < arr.length; j++) {
var item = arr[j];
if (typeof item !== 'string') {
root = item;
}
else {
if (!root[item]) {
root[item] = {};
}
root = root[item];
}
}
root = window;
}
}
then you can create using
ns('fn.method.name');
or
ns('fn.method.name','fn.method.secondName');
and call using
fn.method.name
this function creates your namespace on 'window' so alternatively you can use
window.fn.method.name