Doing a "Diff" on an Associative Array in javascript / jQuery? - javascript

If I have two associative arrays, what would be the most efficient way of doing a diff against their values?
For example, given:
array1 = {
foreground: 'red',
shape: 'circle',
background: 'yellow'
};
array2 = {
foreground: 'red',
shape: 'square',
angle: '90',
background: 'yellow'
};
How would I check one against the other, such that the items missing or additional are the resulting array. In this case, if I wanted to compare array1 within array2, it would return:
array3 = {shape: 'circle'}
Whilst if I compared array2 within array1, it would return:
array3 = {shape: 'square', angle: '90'}
Thanks in advance for your help!

Try this:
function diff(obj1, obj2) {
var result = {};
$.each(obj1, function (key, value) {
if (!obj2.hasOwnProperty(key) || obj2[key] !== obj1[key]) {
result[key] = value;
}
});
return result;
}

If you're familiar with PHP syntax, take a look at http://phpjs.org/functions/index which includes almost all PHP's array related functions converted into JavaScript – including array_diff

RaYell's solution is nice but unfortunately will only tell you the items in obj2 that are either different from or non-existant in obj1, if we need to know both sides, let's get all keys and then compare. The following function will return an associative array with key values for each object. Oh... to be fair, I haven't tested yet, but this should work.
var diff = function(obj1,obj2) {
var newObj = $.extend({},obj1,obj2);
var result = {};
$.each(newObj, function (key, value) {
if (!obj2.hasOwnProperty(key) || !obj1.hasOwnProperty(key) || obj2[key] !== obj1[key]) {
result[key] = [obj1[key],obj2[key]];
}
});
return result;
}
Oh, and while I do recognize that the first solution answered the initial question, I think the above solution offers another approach that the initial user might find useful so as to not require checking twice.

This might be much more sophisticate than what you need, but you can try my jsondiffpatch lib that will diff any pair of javascript objects:
https://github.com/benjamine/jsondiffpatch
if you want to test it you can see it live in http://benjamine.github.com/jsondiffpatch/demo/index.html

A minha ficou assim:
function diff(obj1, obj2){
var result = {};
for(var key1 in obj1){
let resposta = {
before : obj1[key1] ? obj1[key1] : '',
after : obj2[key1] ? obj2[key1] : ''
};
if(resposta.before !== resposta.after){
result[key1] = resposta;
}
}
for(var key2 in obj2){
if(!(key2 in result) || (key2 in obj1)){
let resposta = {
before : obj1[key2] ? obj1[key2] : '',
after : obj2[key2] ? obj2[key2] : ''
}
if(resposta.before !== resposta.after){
result[key2] = resposta;
}
}
}
return (Object.assign({}, result));
}

Related

clone js array w/ objects and primitives

Given the array:
['1', {type:'2'}, ['3', {number: '4'}], '5']
I need to make a clone without using the slice, json.parse and other methods.
At the moment, the code is working, but it will not clone objects:
var myArr =['1',{type:'2'},['3',{number:'4'}],'5'];
var arrClone=[];
for(var i=0;i<myArr.length;i++){
if(typeof(myArr[i])==='object')
{
var arr=[];
for(var j=0;j<myArr[i].length;j++){
arr[j]=myArr[i][j];
}
arrClone[i]=arr;
}else { arrClone[i]=myArr[i]}
}
You could check if an object is supplied and if so, another check is made for an array. Then retrn either the cloned array or the object, or the value itself.
function getClone(value) {
if (value && typeof value === 'object') {
return Array.isArray(value)
? value.map(getClone)
: Object.assign(
...Object.entries(value).map(([k, v]) => ({ [k]: getClone(v) }))
);
}
return value;
}
var data = ['1', { type: '2' }, ['3', { number: '4' }], '5'],
clone = getClone(data);
console.log(getClone(data));
Here is a simple implementation without Array methods:
function deepClone(obj) {
if (typeof obj !== "object" || obj === null) return obj; // primitives
// It could be an array or plain object
const result = obj.constructor.name == "Array" ? [] : {};
for (const key in obj) {
result[key] = deepClone(obj[key]); // recursive call
}
return result;
}
// Demo
var myArr =['1',{type:'2'},['3',{number:'4'}],'5'];
var arrClone = deepClone(myArr);
console.log(arrClone);
Note that this only works for simple data types. As soon as you start to work with Dates, regex objects, Sets, Maps, ...etc, you need much more logic. Also think of self references and functions, and how they should be dealt with.
For more advanced cloning see How to Deep Clone in JavaScript, but expect the use of several methods.

How to use Underscore.js filter with an object?

I have an object like so:
> Object
> Rett#site.com: Array[100]
> pel4#gmail.com: Array[4]
> 0
id : 132
selected : true
> 1
id : 51
selected : false
etc..
How can I use the underscore _.filter() to return back only the items where selected === true?
I've never had the need to go down to layers with _.filter(). Something like
var stuff = _.filter(me.collections, function(item) {
return item[0].selected === true;
});
Thank you
If you want to pull all array elements from any e-mail address where selected is true, you can iterate like so:
var selected = [];
for (email in emailLists) {
selected.concat(_.filter(emailLists[email], function (item) {
return item.selected === true;
}));
}
If you only want to pull the arrays where all elements are selected, you might instead do something like this:
var stuff = _.filter(me.collections, function(item) {
return _.all(item, function (jtem) {
jtem.selected === true;
});
});
Underscore's filter method will work on an object being used as a hash or dictionary, but it will return an array of the object's enumerable values and strip out the keys. I needed a function to filter a hash by its values that would preserve the keys, and wrote this in Coffeescript:
hash_filter: (hash, test_function) ->
keys = Object.keys hash
filtered = {}
for key in keys
filtered[key] = hash[key] if test_function hash[key]
filtered
If you're not using Coffeescript, here's the compiled result in Javascript, cleaned up a little:
hash_filter = function(hash, test_function) {
var filtered, key, keys, i;
keys = Object.keys(hash);
filtered = {};
for (i = 0; i < keys.length; i++) {
key = keys[i];
if (test_function(hash[key])) {
filtered[key] = hash[key];
}
}
return filtered;
}
hash = {a: 1, b: 2, c: 3};
console.log((hash_filter(hash, function(item){return item > 1;})));
// Object {b=2, c=3}
TL; DR: Object.keys() is great!
I have an object called allFilterValues containing the following:
{"originDivision":"GFC","originSubdivision":"","destinationDivision":"","destinationSubdivision":""}
This is ugly but you asked for an underscore based way to filter an object. This is how I returned only the filter elements that had non-falsy values; you can switch the return statement of the filter to whatever you need:
var nonEmptyFilters = _.pick.apply({}, [allFilterValues].concat(_.filter(_.keys(allFilterValues), function(key) {
return allFilterValues[key];
})));
Output (JSON/stringified):
{"originDivision":"GFC"}
#Dexygen was right to utilize _.pick but a cleaner solution is possible because the function also accepts a predicate
Return a copy of the object, filtered to only have values for the allowed keys (or array of valid keys). Alternatively accepts a predicate indicating which keys to pick.
(highlight is mine)
Here's a real life example I've used in a project
_.pick({red: false, yellow: true, green: true}, function(value, key, object) {
return value === true;
});
// {yellow: true, green: true}
const obj = {
1 : { active: true },
2 : { active: false },
3 : { active: false },
}
let filtered = Object.entries(obj).reduce((acc, current) => {
const currentEntry = current[1];
const currentKey = current[0];
//here you check condition
if (currentEntry.active) {
return {
...acc,
[currentKey]: currentEntry
}
}
return acc;
}, {})
There is a rule of thumb, if you need to achieve something really exotic look up into reducer it can solve almost all problems related to objects, it's a bit tricky to get used to it, but trust me thorough reading of documentation gonna pay off.
Maybe you want a simplest way
_.filter(me.collections, { selected: true})

Rename the keys in an object

var addObjectResponse = [{
'SPO2': '222.00000',
'VitalGroupID': 1152,
'Temperature': 36.6666666666667,
'DateTimeTaken': '/Date(1301494335000-0400)/',
'UserID': 1,
'Height': 182.88,
'UserName': 'Admin',
'BloodPressureDiastolic': 80,
'Weight': 100909.090909091,
'TemperatureMethod': 'Oral',
'Resprate': 111,
'HeartRate': 111,
'BloodPressurePosition': 'Standing',
'VitalSite': 'Popliteal',
'VitalID': 1135,
'Laterality': 'Right',
'HeartRateRegularity': 'Regular',
'HeadCircumference': '',
'BloodPressureSystolic': 120,
'CuffSize': 'XL',
}];
How to rename the keys... like SPO2 into O2... there are such many objects in the array...
maybe something like this?
var i, len = addObjectResponse.length;
for (i = 0; i < len; i++) {
addObjectResponse[i]['O2'] = addObjectResponse[i]['SPO2'];
delete addObjectResponse[i]['SPO2'];
}
or
addObjectResponse = addObjectResponse.map(function (obj) {
obj['O2'] = obj['SP02'];
delete obj['S02'];
return obj;
});
or
for (let obj of addObjectResponse) {
obj['O2'] = obj['SP02'];
delete obj['S02'];
}
or
function renameProperty(obj, fromKey, toKey) {
obj[toKey] = obj[fromKey];
delete obj[fromKey];
}
addObjectResponse.forEach(obj => renameProperty(obj, 'SP02', 'O2'));
You cannot directly rename the properties. However, you can set new properties and unset the old ones, indirectly "renaming" them:
function rename(obj, oldName, newName) {
if(!obj.hasOwnProperty(oldName)) {
return false;
}
obj[newName] = obj[oldName];
delete obj[oldName];
return true;
}
Immutable key renaming in vanilla JS one-liner
This may not be the most efficient way to rename a key but I think it's interesting in certain ways:
It doesn't mutate the original objects
It takes one line of vanilla JavaScript
It demonstrates the use of modern syntax
No.1 may sometimes be needed if you still need to use the original array.
No.2 may be interesting considering the fact that some of the examples here have more than 30 lines of code.
No.3 may serve an educational purpose to demostrate some of the features of the language that are not used as often as they should, considering the fact how powerful and how widely supported they are.
If you create a mapping object like this:
const m = { SPO2: 'O2' };
then you'll be able to add more keys to rename in the future easily.
Now, can create a one-liner in vanilla JS:
const t = o => Object.assign(...Object.keys(o).map(k => ({ [m[k] || k]: o[k] })));
Let's say that you have an array of objects:
const a = [{
'SPO2': '222.00000',
'VitalGroupID': 1152,
}, {
'SPO2': '333.00000',
'VitalGroupID': 1153,
}, {
'SPO2': '444.00000',
'VitalGroupID': 1154,
}];
You can get a new array with a.map(t) like this:
console.log('Before:', a);
console.log('After:', a.map(t));
Your original objects are still intact in the original array.
I have created a nice function to rename properties names: https://github.com/meni181818/simpleCloneJS/blob/master/renameProperties.js
usage:
var renamedObj = renameProperties(sourceObject, {propName: 'propNEWname', anotherPropName: 'anotherPropNEWname'});
My function, also handles objects inside arrays so in your case you can do:
addObjectResponse = renameProperties(addObjectResponse, {SPO2: 'O2'});
DEMO
function renameProperties(sourceObj, replaceList, destObj) {
destObj = destObj || {};
each(sourceObj, function(key) {
if(sourceObj.hasOwnProperty(key)) {
if(sourceObj[key] instanceof Array) {
if(replaceList[key]) {
var newName = replaceList[key];
destObj[newName] = [];
renameProperties(sourceObj[key], replaceList, destObj[newName]);
} else if(!replaceList[key]) {
destObj[key] = [];
renameProperties(sourceObj[key], replaceList, destObj[key]);
}
} else if(typeof sourceObj[key] === 'object') {
if(replaceList[key]) {
var newName = replaceList[key];
destObj[newName] = {};
renameProperties(sourceObj[key], replaceList, destObj[newName]);
} else if(!replaceList[key]) {
destObj[key] = {};
renameProperties(sourceObj[key], replaceList, destObj[key]);
}
} else {
if(replaceList[key]) {
var newName = replaceList[key];
destObj[newName] = sourceObj[key];
} else if(!replaceList[key]) {
destObj[key] = sourceObj[key];
}
}
}
});
return destObj;
}
on line 3 in the function above, we using each() function. which is this:
function each(objOrArr, callBack) {
if(objOrArr instanceof Array) {
for(var i = 0; i < objOrArr.length; i++) {
callBack(i);
}
} else if(typeof objOrArr === 'object') {
for(var prop in objOrArr) {
// if the property really exist
if(objOrArr.hasOwnProperty(prop)) {
callBack(prop);
}
}
}
}
note: If you are using Jquery OR underscore.js Or another library that has 'each()' function, you can use it Instead. just replece to $.each (jquery) or _.each (underscore.js).
Ok, so there's two things you're doing here, iterating through an array and renaming properties of an object.
Firstly, to itterate you should generally be using the arrays map() function.
It's less error prone than using a for ( .. ) loop and slightly nicer than forEach(), I think.
A for ( .. ) loop will usually give you better performance (depending on the JS engine) but you need to be dealing with pretty massive array to notice (ie. maybe a ~10ms difference for 100k elements).
Secondly, to rename a object property, the obvious solution is to just set the new key and deleting the old.
This will work but won't always give you properties that behave exactly like the old one if a custom getter or setter has been defined.
If you're creating a generic helper function to do this kind of work you'd be better off using
Object.defineProperty() and
Object.getOwnPropertyDescriptor().
Putting this together we get:
function renameKeyInObjArray (array, oldKey, newKey) {
return array.map(function (obj) {
Object.defineProperty(obj, newKey, Object.getOwnPropertyDescriptor(obj, oldKey));
delete obj[oldKey];
return obj;
});
}
// Use our new function to process the array
renameKeyInObjArray(addObjectResponse, 'SPO2', 'O2');
This function updates the contents of the array by reference and also returns a reference to the array, so can be chained. It's also written in ES5.1 syntax so should run pretty much everywhere.
Here's one that works over an array of objects and takes a map of old object keys to new object keys.
I mostly copied the very nice code from here and just made it operate over arrays of objects rather than a single one.
Code
const renameKeys = (keysMap, objArr) =>
(renamedArr = objArr.map((obj) =>
Object.keys(obj).reduce(
(acc, key) => ({
...acc,
...{ [keysMap[key] || key]: obj[key] },
}),
{}
)
));
Example
renameKeys({ tWo: 'two', FreE: 'three' }, [
{ one: 1, tWo: 2, three: 3 },
{ one: 100, two: 200, FreE: 300 },
]);
[ { one: 1, two: 2, three: 3 }, { one: 100, two: 200, three: 300 } ]
You can add + delete (read the IE caveat);
var addObjectResponse = [{
'SPO2': '222.00000',
'VitalGroupID': 1152
}]
for (var k in addObjectResponse[0])
log(k)
>>SPO2
>>VitalGroupID
addObjectResponse[0]['O2'] = addObjectResponse[0]['SPO2']
delete addObjectResponse[0]['SPO2']
for (var k in addObjectResponse[0])
log(k)
>>VitalGroupID
>>O2
addObjectResponse[0]["O2"] = addObjectResponse[0]["SPO2"];
addObjectResponse[0]["SP02"] = null;
The [0] is necessary because addObjectResponse is set to an array with one element, which contains an object. Do you have any rules as to what keys will be renamed or how?
Edit: I misunderstood the OP, thinking that "many objects" referred to many keys in the object that need to be renamed, as opposed to many objects in the array that each need to have that one key renamed.
Instead of renaming this object key, you could create another object with proper names, like this:
var obj={wrongKeyName:'test'};
var obj2 = {}
obj2.rightKeyName = obj.wrongKeyName;
console.log(obj2);
A little late to the game here but how about something like this:
const newAddObjectResponse = addObjectResponse.map((obj) => {
const {SPO2: O2, ...rest} = obj
return {O2, ...rest}
})
If you want to replace your original array then you could do:
let addObjectResponse = [
{
SPO2: '222.00000',
VitalGroupID: 1152,
Temperature: 36.6666666666667,
DateTimeTaken: '/Date(1301494335000-0400)/',
UserID: 1,
Height: 182.88,
UserName: 'Admin',
BloodPressureDiastolic: 80,
Weight: 100909.090909091,
TemperatureMethod: 'Oral',
Resprate: 111,
HeartRate: 111,
BloodPressurePosition: 'Standing',
VitalSite: 'Popliteal',
VitalID: 1135,
Laterality: 'Right',
HeartRateRegularity: 'Regular',
HeadCircumference: '',
BloodPressureSystolic: 120,
CuffSize: 'XL',
},
]
addObjectResponse = addObjectResponse.map((obj) => {
const {SPO2: O2, ...rest} = obj
return {O2, ...rest}
})

Sorting a JavaScript object by property name

I've been looking for a while and want a way to sort a Javascript object like this:
{
method: 'artist.getInfo',
artist: 'Green Day',
format: 'json',
api_key: 'fa3af76b9396d0091c9c41ebe3c63716'
}
and sort is alphabetically by name to get:
{
api_key: 'fa3af76b9396d0091c9c41ebe3c63716',
artist: 'Green Day',
format: 'json',
method: 'artist.getInfo'
}
I can't find any code that will do this. Can anyone give me some help?
UPDATE from the comments:
This answer is outdated. In ES6 objects keys are now ordered. See this question for an up-to-date answer
By definition, the order of keys in an object is undefined, so you probably won't be able to do that in a way that is future-proof. Instead, you should think about sorting these keys when the object is actually being displayed to the user. Whatever sort order it uses internally doesn't really matter anyway.
By convention, most browsers will retain the order of keys in an object in the order that they were added. So, you could do this, but don't expect it to always work:
function sortObject(o) {
var sorted = {},
key, a = [];
for (key in o) {
if (o.hasOwnProperty(key)) {
a.push(key);
}
}
a.sort();
for (key = 0; key < a.length; key++) {
sorted[a[key]] = o[a[key]];
}
return sorted;
}
this function takes an object and returns a sorted array of arrays of the form [key,value]
function (o) {
var a = [],i;
for(i in o){
if(o.hasOwnProperty(i)){
a.push([i,o[i]]);
}
}
a.sort(function(a,b){ return a[0]>b[0]?1:-1; })
return a;
}
The object data structure does not have a well defined order. In mathematical terms, the collection of keys in an object are an Unordered Set, and should be treated as such.
If you want to define order, you SHOULD use an array, because an array having an order is an assumption you can rely on. An object having some kind of order is something that is left to the whims of the implementation.
Just use sorted stringify() when you need to compare or hash the results.
// if ya need old browser support
Object.keys = Object.keys || function(o) {
var result = [];
for(var name in o) {
if (o.hasOwnProperty(name))
result.push(name);
}
return result;
};
var o = {c: 3, a: 1, b: 2};
var n = sortem(o);
function sortem(old){
var newo = {}; Object.keys(old).sort().forEach(function(k) {new[k]=old[k]});
return newo;
}
// deep
function sortem(old){
var newo = {}; Object.keys(old).sort().forEach(function(k){ newo[k]=sortem(old[k]) });
return newo;
}
sortem({b:{b:1,a:2},a:{b:1,a:2}})
Here is a one-liner for you.
Array.prototype.reduce()
let data = {
method: 'artist.getInfo',
artist: 'Green Day',
format: 'json',
api_key: 'fa3af76b9396d0091c9c41ebe3c63716'
};
let sorted = Object.keys(data).sort().reduce( (acc, currValue) => {
acc[currValue] = data[currValue];
return acc;
}, {});
console.log(sorted);
Good luck!!
ES5 Compatible:
function sortByKey(obj) {
var keys = Object.keys(obj);
keys.sort();
var sorted = {};
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
sorted[key] = obj[key];
}
return sorted;
}
This should be used with caution as your code shouldn't rely on Object properties order. If it's just a matter of presentation (or just for the fun !), you can sort properties deeply like this :
function sortObject(src) {
var out;
if (typeof src === 'object' && Object.keys(src).length > 0) {
out = {};
Object.keys(src).sort().forEach(function (key) {
out[key] = sortObject(src[key]);
});
return out;
}
return src;
}

Getting the first index of an object

Consider:
var object = {
foo: {},
bar: {},
baz: {}
}
How would I do this:
var first = object[0];
console.log(first);
Obviously, that doesn’t work because the first index is named foo,
not 0.
console.log(object['foo']);
works, but I don’t know it’s named foo. It could be named anything. I just want the first.
Just for fun this works in JS 1.8.5
var obj = {a: 1, b: 2, c: 3};
Object.keys(obj)[0]; // "a"
This matches the same order that you would see doing
for (o in obj) { ... }
If you want something concise try:
for (first in obj) break;
alert(first);
wrapped as a function:
function first(obj) {
for (var a in obj) return a;
}
they're not really ordered, but you can do:
var first;
for (var i in obj) {
if (obj.hasOwnProperty(i) && typeof(i) !== 'function') {
first = obj[i];
break;
}
}
the .hasOwnProperty() is important to ignore prototyped objects.
This will not give you the first one as javascript objects are unordered, however this is fine in some cases.
myObject[Object.keys(myObject)[0]]
If the order of the objects is significant, you should revise your JSON schema to store the objects in an array:
[
{"name":"foo", ...},
{"name":"bar", ...},
{"name":"baz", ...}
]
or maybe:
[
["foo", {}],
["bar", {}],
["baz", {}]
]
As Ben Alpert points out, properties of Javascript objects are unordered, and your code is broken if you expect them to enumerate in the same order that they are specified in the object literal—there is no "first" property.
for first key of object you can use
console.log(Object.keys(object)[0]);//print key's name
for value
console.log(object[Object.keys(object)[0]]);//print key's value
There is no way to get the first element, seeing as "hashes" (objects) in JavaScript have unordered properties. Your best bet is to store the keys in an array:
var keys = ["foo", "bar", "baz"];
Then use that to get the proper value:
object[keys[0]]
ES6
const [first] = Object.keys(obj)
Using underscore you can use _.pairs to get the first object entry as a key value pair as follows:
_.pairs(obj)[0]
Then the key would be available with a further [0] subscript, the value with [1]
I had the same problem yesterday. I solved it like this:
var obj = {
foo:{},
bar:{},
baz:{}
},
first = null,
key = null;
for (var key in obj) {
first = obj[key];
if(typeof(first) !== 'function') {
break;
}
}
// first is the first enumerated property, and key it's corresponding key.
Not the most elegant solution, and I am pretty sure that it may yield different results in different browsers (i.e. the specs says that enumeration is not required to enumerate the properties in the same order as they were defined). However, I only had a single property in my object so that was a non-issue. I just needed the first key.
You could do something like this:
var object = {
foo:{a:'first'},
bar:{},
baz:{}
}
function getAttributeByIndex(obj, index){
var i = 0;
for (var attr in obj){
if (index === i){
return obj[attr];
}
i++;
}
return null;
}
var first = getAttributeByIndex(object, 0); // returns the value of the
// first (0 index) attribute
// of the object ( {a:'first'} )
To get the first key of your object
const myObject = {
'foo1': { name: 'myNam1' },
'foo2': { name: 'myNam2' }
}
const result = Object.keys(myObject)[0];
// result will return 'foo1'
Based on CMS answer. I don't get the value directly, instead I take the key at its index and use this to get the value:
Object.keyAt = function(obj, index) {
var i = 0;
for (var key in obj) {
if ((index || 0) === i++) return key;
}
};
var obj = {
foo: '1st',
bar: '2nd',
baz: '3rd'
};
var key = Object.keyAt(obj, 1);
var val = obj[key];
console.log(key); // => 'bar'
console.log(val); // => '2nd'
My solution:
Object.prototype.__index = function(index)
{
var i = -1;
for (var key in this)
{
if (this.hasOwnProperty(key) && typeof(this[key])!=='function')
++i;
if (i >= index)
return this[key];
}
return null;
}
aObj = {'jack':3, 'peter':4, '5':'col', 'kk':function(){alert('hell');}, 'till':'ding'};
alert(aObj.__index(4));

Categories