How to determine equality of values of JavaScript object? - javascript

In the given object all values of properties are the same.
var obj = {
property1: 'Some Value',
property2: 'Some Value',
property3: 'Some Value',
property4: 'Some Value'
}
The function checkEquality should return true if all values are the same and false otherwise.
I can do the following to achieve it:
function checkEquality () {
var compare = obj.propery1;
for (var key in obj) {
if (obj[key] !== compare) {
return false;
}
}
return true;
}
But this solution by far not the best.

You could use Array#every for it.
The every method executes the provided callback function once for each element present in the array until it finds one where callback returns a falsy value (a value that becomes false when converted to a Boolean). If such an element is found, the every method immediately returns false. Otherwise, if callback returned a true value for all elements, every will return true. callback is invoked only for indexes of the array which have assigned values; it is not invoked for indexes which have been deleted or which have never been assigned values.
var obj = { propery1: 'Some Value', propery2: 'Some Value', propery3: 'Some Value', propery4: 'Some Value' },
equal = Object.keys(obj).every(function (k, i, kk) {
return !i || obj[kk[0]] === obj[k];
});
console.log(equal);

Great answer by #Nina, I'd just like to add something different using reduce and ES6 syntax:
const sameValues = (obj ) => {
let keys = Object.keys( obj );
let initial = obj[ keys[0] ];
// val will be either the initial value or false
let val = keys.reduce( (prev, cur) => ( obj[cur] === prev ? prev : false ), initial );
return val === initial;
}
https://jsfiddle.net/9tb5mdoL/

Related

If array contains different type elements, how to pick the same type elements and create new array with it?

Hi! So I want to get the following result:
enter image description here
and here's the js function that i wrote. The problem is that the output of my function is null.
function dataTypeArray(arr, type){
for(i=0; i<=arr.length-1; i++)
var string = [];
var number = [];
var boolean = [];
var i;
if(type = 'string' && typeof arr[i] == 'string'){
string.push(arr[i]);
} else {
if(type = 'number' && typeof arr[i] == 'number'){
number.push(arr[i]);
} else {
if(type = 'boolean' && typeof arr[i] == 'boolean'){
boolean.push(arr[i]);
} else {
return 'null';
}}}
return string, number, boolean;}
var arr = ['hhg', 'fthth', 456, true];
console.log(dataTypeArray(arr, 'string'));
I'd suggest using Array.prototype.filter.
var arr = ['hhg', 'fthth', 456, true];
var strings = arr.filter(x => typeof x === 'string');
var numbers = arr.filter(x => typeof x === 'number');
var booleans = arr.filter(x => typeof x === 'boolean');
Rajesh has already listed in their comment what is going wrong in your current code. I'd recommend you to move to modern JavaScript, the task becomes quite easy:
function getTypeMap(arr) {
const typeMap = {};
for (const val of arr) {
const key = `${typeof val}s`;
typeMap[key]?.push(val) ?? (typeMap[key] = [val]);
}
return typeMap;
}
const exampleArr = [0, 'a', 1, 'b', true, 2, false],
{booleans, numbers, strings} = getTypeMap(exampleArr);
console.log(booleans, numbers, strings);
As said, you can't return multiple values from a function. Here we create an object with keys by types of the values in the array, and return the object. In the loop, the code checks whether a property named by the type of the value exists using optional chaining operator (s is added at the end of the type because you can't create variables named function and undefined). If the operator returns undefined we'll create a new array with the current value in the first index (see nullish coalescing operator), otherwise we just push a new value to the type array.
Properties from the returned object are then assigned to variables using destructuring assignment ({booleans, numbers, strings} = ...). This way you can get any array of the types, just pick those you need when destructuring the returned object to the variables, or store the returned object to a variable, and use its properties where needed.
If you need more accurate type than typeof operator can return, you can do something like at this jsFiddle.
One possible generic approach could be based on reduce and this method's 2nd initialValue parameter which will be passed to (and continued being passed around by) the reducer function.
This reducer is implemented around the initially passed object which features both a result object where the final result gets aggregated and a detection property which is a fully customizable key value based configuration of type detection functions.
Thus one implements the reducer once and then configures its usage at will.
function detectAndCollectType({ detection, result }, type) {
const isAnyMatch = Object
.entries(detection)
.some(([ key, isType ]) => {
const doesMatch = isType(type);
if (doesMatch) {
(result[key] ??= []).push(type);
}
return doesMatch;
});
if (!isAnyMatch) {
(result.unhandled ??= []).push(type);
}
return { detection, result };
}
// 1st usage example.
const { result } = [null, void 0, 0, 'a', 1, 'b', true, 2, false, new Date]
.reduce(detectAndCollectType, {
detection: {
boolean: val => 'boolean' === typeof val,
number: val => 'number' === typeof val,
string: val => 'string' === typeof val,
},
result: {},
});
console.log({ result });
// 2nd usage example.
const { result: {
nullish,
boolean,
number,
string,
date,
}} = [null, void 0, 0, 'a', 1, 'b', true, 2, false, new Date]
.reduce(detectAndCollectType, {
detection: {
nullish: val => (val ?? null) === null,
boolean: val => 'boolean' === typeof val,
number: val => 'number' === typeof val,
string: val => 'string' === typeof val,
date: val => '[object Date]' === Object.prototype.toString.call(val),
},
result: {},
});
console.log({
nullish,
boolean,
number,
string,
date,
});
.as-console-wrapper { min-height: 100%!important; top: 0; }
Edit ... in order to reflect the discussion with #Teemu
As it has been discussed in the comments below, there is another generic reduce based approach which is more strict in terms of type detection due to its straightforward implementation which extracts a type's (internal) class name and collects any array item accordingly.
Here everything depends on a proper type detection which is a very specific field on its own.
// helper function for exposing a value's (internal) class name.
function getClassName(value) {
const regXClassName =
// ... [https://regex101.com/r/flUvPh/1]
(/class\s+(?<customName>[^\s{]+)(?:\s+extends\s+\S+)?\s*\{|function\s+(?<builtInName>[^\s(]+)\s*\(/);
let customName, builtInName;
if ((value ?? null) !== null) {
({ customName, builtInName } = regXClassName
.exec(String(Object.getPrototypeOf(value).constructor)).groups ?? {});
}
return customName || builtInName || (/\[object\s+(?<className>[^\]]+)\]/)
.exec(Object.prototype.toString.call(value))
?.groups.className;
}
// the above helper is part of the following SO answer
// ... [https://stackoverflow.com/questions/73421732/how-to-know-what-class-inheritance-is-object-from-database/73775470#73775470]
function detectAndCollectTypeByItsClassName(result, type) {
const key = getClassName(type)
// first character to lower case.
.replace(/./, match => match.toLowerCase());
(result[key] ??= []).push(type);
return result;
}
console.log(
['hhg', 'fthth', 456, true]
.reduce(detectAndCollectTypeByItsClassName, {})
);
class CustomDate extends Date {}
console.log(
[null, void 0, 0, 'a', 1, 'b', true, 2, null, false, new Date, new CustomDate]
.reduce(detectAndCollectTypeByItsClassName, {})
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

Indexing a JSON by index instead of key? [duplicate]

If I have an array like this:
var arr = ['one','two','three'];
I can access different parts by doing this:
console.log(arr[1]);
How can I access object properties by their order rather than by key?
Example:
var obj = {
'something' : 'awesome',
'evenmore' : 'crazy'
},
jbo = {
'evenmore' : 'crazy',
'something' : 'awesome'
};
How would I get the first property for each object–"something" from obj and "evenmore" from jbo–without explicitly using the property name?
Now, a few of you seem to think I'm after something like:
console.log(obj['something']);
This is not the case, I'm specifically looking to target the index, just like the first example - if it's possible.
"I'm specifically looking to target the index, just like the first example - if it's possible."
No, it isn't possible.
The closest you can get is to get an Array of the object's keys, and use that:
var keys = Object.keys( obj );
...but there's no guarantee that the keys will be returned in the order you defined. So it could end up looking like:
keys[ 0 ]; // 'evenmore'
keys[ 1 ]; // 'something'
The only way I can think of doing this is by creating a method that gives you the property using Object.keys();.
var obj = {
dog: "woof",
cat: "meow",
key: function(n) {
return this[Object.keys(this)[n]];
}
};
obj.key(1); // "meow"
Demo: http://jsfiddle.net/UmkVn/
It would be possible to extend this to all objects using Object.prototype; but that isn't usually recommended.
Instead, use a function helper:
var object = {
key: function(n) {
return this[ Object.keys(this)[n] ];
}
};
function key(obj, idx) {
return object.key.call(obj, idx);
}
key({ a: 6 }, 0); // 6
You can use the Object.values() method if you dont want to use the Object.keys().
As opposed to the Object.keys() method that returns an array of a given object's own enumerable properties, so for instance:
const object1 = {
a: 'somestring',
b: 42,
c: false
};
console.log(Object.keys(object1));
Would print out the following array:
[ 'a', 'b', 'c' ]
The Object.values() method returns an array of a given object's own enumerable property values.
So if you have the same object but use values instead,
const object1 = {
a: 'somestring',
b: 42,
c: false
};
console.log(Object.values(object1));
You would get the following array:
[ 'somestring', 42, false ]
So if you wanted to access the object1.b, but using an index instead you could use:
Object.values(object1)[1] === 42
You can read more about this method here.
var obj = {
'key1':'value',
'2':'value',
'key 1':'value'
}
console.log(obj.key1)
console.log(obj['key1'])
console.log(obj['2'])
console.log(obj['key 1'])
// will not work
console.log(obj.2)
Edit:
"I'm specifically looking to target the index, just like the first example - if it's possible."
Actually the 'index' is the key. If you want to store the position of a key you need to create a custom object to handle this.
Yes, it is possible. We can define getters for each index, and return the property value, in the constructor method of the class. See this code.
class className {
constructor() {
this.name = "Bikram";
this.age = 8;
this.x = 89;
this.y = true;
//Use a for loop and define the getters (with the object property's index as its "name") for each property using Object.defineProperty()
for (let i = 0; i < Object.keys(this).length; i++) {
Object.defineProperty(this, i, {
get: function() {
return Object.values(this)[i]}
});
}
}
}
var b = new className();
console.log(b[0]); // same as b.name ("Bikram")
console.log(b[1]); // = b.age (8)
console.log(b[2]); // = b.x (89)
console.log(b[3]); // = b.y (true)
Edit: If you want to change the properties by their indices, which, of course, you do. Then, just define a corresponding setter for each property in the Object.defineProperty() method. It will look like:
// Insert this in place of the old one
Object.defineProperty(this, i, {
get: function() {
return Object.values(this)[i];
},
set: function(newValue) {
this[Object.keys(this)[i]] = newValue;
}
})
console.log(b[0]); // "Bikram"
b[0] = "Bikram Kumar";
console.log(b[0]); // "Bikram Kumar"
And now you have an "array-like-object" whose properties can be accessed or modified either by property key or its index :D
A side note: Notice that Object.keys() and Object.values() only return the enumerable properties. If you just declare a property and not assign it to any value, the Object.[key/value]s() methods will leave that in the returned array, because by default they are not enumerable. This might become confusing for the indices so defined (except the case the undeclared property is the last one).
To get around this, there is a simple way, if you want some property to have a index, but don't wanna assign it now. Just set it to undefined, and it will now be enumerable, and the indices won't be affected.
by jquery you can do this:
var arr = $.map(obj,function(value, key) {
return value;
});
alert(obj[0]);
Get the array of keys, reverse it, then run your loop
var keys = Object.keys( obj ).reverse();
for(var i = 0; i < keys.length; i++){
var key = keys[i];
var value = obj[key];
//do stuff backwards
}
you can create an array that filled with your object fields and use an index on the array and access object properties via that
propertiesName:['pr1','pr2','pr3']
this.myObject[this.propertiesName[0]]
I went ahead and made a function for you:
Object.prototype.getValueByIndex = function (index) {
/*
Object.getOwnPropertyNames() takes in a parameter of the object,
and returns an array of all the properties.
In this case it would return: ["something","evenmore"].
So, this[Object.getOwnPropertyNames(this)[index]]; is really just the same thing as:
this[propertyName]
*/
return this[Object.getOwnPropertyNames(this)[index]];
};
let obj = {
'something' : 'awesome',
'evenmore' : 'crazy'
};
console.log(obj.getValueByIndex(0)); // Expected output: "awesome"
Sure it is possible, but it is not as immediate as accessing to an array by its indexes, but still possible and even relatively simple actually: in fact you don't have to struggle too much. This code sample will show how:
var obj = {
'alfa' : 'value of obj the key alfa',
'beta' : 'value of obj the key beta',
'gamma' : 'value of obj the key gamma'
};
var jbo = {
'alfa' : 'value of jbo the key alfa',
'beta' : 'value of jbo the key beta',
'gamma' : 'value of jbo the key gamma'
};
alert ( obj[Object.keys(obj)[1]] );
alert ( jbo[Object.keys(jbo)[1]] );
/* you can even put it into a for loop as follows */
for (i=0;i<3;i++)
{
document.writeln ( "<br>This could be even a piece of HTML: " + obj[Object.keys(obj)[i]] );
document.writeln ( "<br>This could be even a piece of HTML: " + jbo[Object.keys(jbo)[i]] );
}
Explication:
As you know the Object.keys() statement returns an array of all enumerable properties (which means all keys) of the object you type into its round parenthesis.
So the only thing you need is to indicate the index after that array, which will returns the key literal found at that index.
The key itself is "digested" as usual by the object which returns the value at that key.
If you are not sure Object.keys() is going to return you the keys in the right order, you can try this logic instead
var keys = []
var obj = {
'key1' : 'value1',
'key2' : 'value2',
'key3' : 'value3',
}
for (var key in obj){
keys.push(key)
}
console.log(obj[keys[1]])
console.log(obj[keys[2]])
console.log(obj[keys[3]])
You can also construct a function that will return the value of a property by accepting two parameters: the object and the "index" (order position)
function getValue(obj, index) {
let keysArray = Object.keys(obj)
let key = keysArray[index]
return obj[key]
}
Usage example getValue(obj, 2)
Snippet
let obj = {a: 'dog', b: 'cat', c: 'mouse'}
function getValue(obj, index){
let keysArray = Object.keys(obj)
let key = keysArray[index]
return obj[key]
}
console.log(getValue(obj, 2))

Check if specific object is empty in typescript

How to check if an object is empty?
ex:
private brand: Brand = new Brand();
I tried:
if (this.brand) {
console.log('is empty');
}
not working.
Use Object.keys(obj).length to check if it is empty.
Output : 3
Source: Object.keys()
You can use Object.keys like this:
class Brand { }
const brand = new Brand();
if (Object.keys(brand).length === 0) {
console.log("No properties")
}
If you want to check if the object has at least one non-null, non-undefined property:
Get all the values of the object in an array using Object.values()
Check if at least one of has value using some
const hasValues =
(obj) => Object.values(obj).some(v => v !== null && typeof v !== "undefined")
class Brand { }
const brand = new Brand();
if (hasValues(brand)) {
console.log("This won't be logged")
}
brand.name = null;
if (hasValues(brand)) {
console.log("Still no")
}
brand.name = "Nike";
if (hasValues(brand)) {
console.log("This object has some non-null, non-undefined properties")
}
You can also use lodash for checking the object
if(_.isEmpty(this.brand)){
console.log("brand is empty")
}
Object.keys(myObject).length == 0
A Map obj can be created with empty properties and size might not work .
Object might not be equal to empty or undefined
But with above code you can find whether an object is really empty or not
Here is a comparison between the 2 most popular answers, they do have slightly different implications:
let o1 = {}
console.log(JSON.stringify(o1) === '{}')
console.log(Object.keys(o1).length === 0)
// true
// true
let o2 = { p: undefined }
console.log(JSON.stringify(o2) === '{}')
console.log(Object.keys(o2).length === 0)
// true
// false
let o3 = { l: null }
console.log(JSON.stringify(o3) === '{}')
console.log(Object.keys(o3).length === 0)
// false
// false
let contacts = {};
if(Object.keys(contacts).length==0){
console.log("contacts is an Empty Object");
}else{
console.log("contacts is Not an Empty Object");
}
This is the fastest construct that I'm aware of, albeit it uses somewhat puzzling for...in loop that doesn't loop (in my tests it's about 2x faster than Object.keys)
export function isObjectEmpty(object: Record<string, unknown>): boolean {
for (const property in object) {
// if any enumerable property is found object is not empty
return false;
}
return true;
}
JSON.stringify(this.brand) === '{}'
Careful about Object.keys and Array.some solutions, in case if your object is not even initialized and worth null.
Also care that there is no key worthing undefined.
const objNotInitialized = null;
console.log(Object.keys(objNotInitialized));
You could add an extra check in that case, leading to the final soluce :
function isEmpty(obj) {
return !obj || !Object.keys(obj).some(x => obj[x] !== void 0);
}
console.log(isEmpty({
x: void 0,
}));
console.log(isEmpty(null));
console.log(isEmpty({
key: 'value',
}));
If you can use Object.values :
function isEmpty(obj) {
return !obj || !Object.values(obj).some(x => x !== void 0);
}
console.log(isEmpty({
x: void 0,
}));
console.log(isEmpty(null));
console.log(isEmpty({
key: 'value',
}));
const obj = {};
// Using Object.keys to loop on the object keys and count them up
if (!Object.keys(obj).length) {
console.log('#1 obj is empty');
}
// What if a key worth undefined ?
const objWithUndefinedKey = {
x: void 0,
};
// Using Object.keys is not enough, we have to check the value behind to remove
// undefined values
if (!Object.keys(objWithUndefinedKey).some(x => objWithUndefinedKey[x] !== void 0)) {
console.log('#2 obj is empty');
}
// Or more elegant using Object.values
if (!Object.values(objWithUndefinedKey).some(x => x !== void 0)) {
console.log('#3 obj is empty');
}
// Alternative is to use for ... in
let empty = true;
for (key in objWithUndefinedKey) {
if (objWithUndefinedKey[key] !== void 0) {
empty = false;
}
}
if (empty) {
console.log('#4 obj is empty');
}
Object.values(this.brand).some(b => b != null);
The good approach is to have a short function that you can use everywhere in your app :
export const isEmpty = (obj) => {
return obj === null || undefined
? true
: (() => {
for (const prop in obj) {
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
return false;
}
}
return true;
})();
};
If you build ECMA 7+ can try
Object.entries(obj).length === 0 && obj.constructor === Object
If you want a fully type-safe answer for this, that allows you to tell tsc that an object is, or is not empty when narrowing a union type, you can create a type guard utility function using one of the above answers as suits your needs:
const isEmpty = (obj: unknown): obj is Record<never, never> =>
typeof obj === 'object' && obj !== null && Object.keys(obj).length === 0;
This allows you to have a type that could be an object with keys or an empty object (or any other type for that matter), and tell tsc that it is (or is not) and empty object by:
Checking whether the passed argument is an object
Checking that it's not null (typeof x === 'object' returns true for null)
Running any other checks that suit your use cases (depending on whether you care about null or undefined values within the object) - in this case the Object.keys().length method.
You can then use this with:
const unknownType: {prop: number} | Record<never, never> =
functionThatCouldReturnAnEmptyObjectOrNot();
// Type guard returns 'true' and 'definitelyEmpty' will be inferred as an empty object
const definitelyEmpty = isEmpty(unknownType) ? unknownType : {};
// Type guard returns 'false', reversed with the '!', and 'definitelyNotEmpty'
// will be inferred as an object of type {prop: number}
const definitelyNotEmpty = !isEmpty(unknownType) ? unknownType : {prop: 1};
// Not using a type guard, tsc will still infer 'notNarrowed' as
// ({prop: number} | Record<never, never>), meaning that using it where an empty
// object (or not empty object) is required will cause a warning.
const notNarrowed = Object.keys(unknownType).length === 0 ? unknownType : {};
Some other potentially useful notes:
Record<never, never> means an empty object ({})
{} as a type annotation (e.g: const empty: {} = {}) actually means 'any non-nullish value' so is only just above any in terms of type safety, and will allow most other values (const empty: {} = 3 would be valid).

How to get an isolated value from an array using JavaScript map?

When I execute the below script, I get an array of values; how to get the value '3' separately out of an array. I use JS map here.
function value() {
return dataLayer.map(function(item, index)
{
return (item.event == 'impressionOnScroll' ? index : false);
})
};
value();
You can e.g. use Array#filter to get only the number-ish values.
var arr = [false, false, false, 3, false];
console.log(arr.filter(Number));
//or
//this will work also for 0
console.log(arr.filter(v => typeof v == 'number'));
Array.prototype.filter runs a function against every value in an array. If you return a truthy value from the array, it is kept. Otherwise, it is discarded.
Here you should check to see if the value is not false:
function value() {
return dataLayer.map(function(item, index)
{
return (item.event == 'impressionOnScroll' ? index : false);
}).filter(value => value !== false);
};

Update Javascript Object, nested Data - insert only if not updateable

Let's say i have the following data
var obj = {
test: 'somedata',
scores: [
{
"points":99,
"id":"x12"
},
{
"points":21,
"id":"x13"
}
],
sites: [
{
"exercises":21,
"sid":"s12"
},
{
"exercises":23,
"sid":"s11"
}
],
history: {
key: 'value',
commits: [
{
id: 1,
value: 'thank you'
}
 ]
}
}
Notice that scores and sites contain arrays with unique elements based on id in scores and based on sid in sites. I want a function that does the following magic:
//will **update** obj.test to 'newdata' and return {test:'newdata'}
magicUpdate(obj, {test:'newdata'})
//will **insert** obj.newkey with with value 'value' and return {newkey: 'value'}
magicUpdate(obj, {newkey: 'value'})
//will do nothing and return {}
magicUpdate(obj, {scores: []})
//will **update** scores[0] and return {scores:[{points:3, id: "x12"}]}, as id "x12" is already in the array at index 0
magicUpdate(obj, {scores:[{points:3, id: "x12"}])
//will **insert** {points:3, id: "x14"} into obj.scores and return {scores:[{points:3, id: "x14"}]}
magicUpdate(obj, {scores:[{points:3, id: "x14"}]})
//will **update** sites[0] and return {sites:[{exercises:22, sid: "s12"}]}, as id "s12" is already in the array at index 0
magicUpdate(obj, {sites:[{exercises:22, sid: "s12"}])
//will **insert** {exercises:10, sid: "s14"} into obj.sites and return {sites:[{exercises:10, sid: "s14"}]}
magicUpdate(obj, {sites:[{exercises:10, sid: "s14"}]})
//and also be recursive ...
//will **update** obj.history.commits[0]
magicUpdate(obj, {'history.commits': [{id:1, value: 'changed'}]});
I have seen .update doing the recursion, but only if one is passing the path which should be determined automatically. Then there is .merge which internally uses _.baseMerge and comes really close to what i need though I do not understand the signature of the function.
_.merge(
{scores:[{id: 12, points:10}, {id: 13, points:10}]},
{scores:[{id: 14, points:10}, {id: 15, points:10}]}
)
// returns {scores:[{id: 14, points:10}, {id: 15, points:10}]} not the fully merged array
Can someone point me to a good direction or has achieved similar things with lodash?
The magicUpdate function you mention in your post could be achieved using lodash functions indeed.
For this implementation, I've used mostly _ .get, _ .set and _ .unionWith though I'm sure it could have been achieved using some others:
// src will be mutated. For simplicity's sake, obj is an object with only one property that represents the changes to make to src
function magicUpdate(src, obj) {
var key = _.first(_.keys(obj)),
value = _.get(obj, key),
srcValue = _.get(src, key),
comparator = function(a, b) {
var idKey = _.isUndefined(a.id) ? 'sid' : 'id';
return a[idKey] === b[idKey];
}
if (_.isArray(srcValue)) {
value = _.unionWith(value, srcValue, comparator);
}
return _.set(src, key, value);
}
As you may have noticed looking at the code, the return type is the mutated object and not what you're asking. I wasn't really sure what you wanted as a return value.
Anyway, Lodash doesn't have a built-in object difference function so it'd be necessary to develop something like that in case you wanted the difference between the old object and the modified one (you'd also have to _ .clone the object first to have a copy and be able to compare).
The idea of the function I present is to try to get the key of obj (it's the key we want to modify in src) and check if it exists and is an array. If so, we just add the two arrays, updating those in src that have the same id in obj. Due to the fact that sites, scores and history had id and sid I had to add some more logic to the comparator of the _.unionWith function.
If key doesn't exist or isn't an array, we just set it in src.
Here you have the fiddle in case you want to play with it. Hope it helps.
UPDATE
My first solution was intended for one property updated at a time. However, it seems that is possible to update more than one at the same time.
One quick solution could be to iterate over the object with the updates and update one property at a time.
function updateProperty(src, obj) {
var key = _.first(_.keys(obj)),
value = _.get(obj, key),
srcValue = _.get(src, key),
comparator = function(a, b) {
var idKey = _.isUndefined(a.id) ? 'sid' : 'id';
return a[idKey] === b[idKey];
}
if (_.isArray(srcValue)) {
value = _.unionWith(value, srcValue, comparator);
}
return _.set(src, key, value);
}
function magicUpdate(obj, src) {
_.forEach(src, function(value, key) {
updateProperty(obj, _.pick(src, key));
});
return obj;
}
Fiddle
I wrote a solution which is recursive and quite performant. See this fiddle.
function mergeRecursive(obj1, obj2) {
if (obj1.constructor == Array) {
for (var i = 0; i < obj1.length; i++) {
if (obj1[i].id == obj2.id) {
obj1[i] = obj2;
return obj1;
}
}
obj1.push(obj2);
return obj1;
}
for (var p in obj2) {
// Property in destination object set; update its value.
if (obj2[p].constructor == Array) {
obj2[p].forEach(function(arrayElement) {
obj1[p] = MergeRecursive(obj1[p], arrayElement);
});
} else if (obj2[p].constructor == Object) {
obj1[p] = MergeRecursive(obj1[p], obj2[p]);
} else {
obj1[p] = obj2[p];
}
}
return obj1;
}

Categories

Resources