Sort Keys in Javascript Object - javascript

I have a Javascript Object that contains a mix of property types including simple strings, objects, arrays of objects... and so on.
I would like to sort the keys following this rule:
'Simple properties like strings or numbers appears always before more complex properties that contains arrays or objects'
I wrote the following function, that almost do what I am trying to achieve, but it converts the arrays into objects. This is not the desired behaviour. Can anybody help me to create a function that keep arrays as arrays and at the same time it sorts the objects inside arrays?
function sort(object){
if (typeof object != "object" )
return object;
var keys = Object.keys(object);
keys.sort(function(a,b){
if (typeof(object[a])!== 'object') { return -1 } else { return 1 }
});
Working jsfiddle:
http://jsfiddle.net/u01mn2py/3/
Kind Regards

As of ECMAScript 2015 (ES6), an object's own properties do have order for some operations, although relying on it is rarely a good idea. If you want order, usually it's best to use an array or similar.
The order is:
Let keys be a new empty List.
For each own property key P of O that is an integer index, in ascending numeric index order
Add P as the last element of keys.
For each own property key P of O that is a String but is not an integer index, in property creation order
Add P as the last element of keys.
For each own property key P of O that is a Symbol, in property creation order
Add P as the last element of keys.
Return keys.
That's for "own" properties. I don't think there are any externally-available operations that define a required order for all properties including inherited ones. (for-in is not required to follow the order above, not even in ES2015+.) As of ES2019, for-in does have a defined order (with some exceptions).
This means that it's probably possible to do what you asked, on a compliant engine, provided none of our keys qualifies as as an integer index.
JSON still has no order, but JSON.stringify is required by the JavaScript spec to use the order described above.
I'm not saying I suggest it. :-)
function sort(object) {
// Don't try to sort things that aren't objects
if (typeof object != "object") {
return object;
}
// Don't sort arrays, but do sort their contents
if (Array.isArray(object)) {
object.forEach(function(entry, index) {
object[index] = sort(entry);
});
return object;
}
// Sort the keys
var keys = Object.keys(object);
keys.sort(function (a, b) {
var atype = typeof object[a],
btype = typeof object[b],
rv;
if (atype !== btype && (atype === "object" || btype === "object")) {
// Non-objects before objects
rv = atype === 'object' ? 1 : -1;
} else {
// Alphabetical within categories
rv = a.localeCompare(b);
}
return rv;
});
// Create new object in the new order, sorting
// its subordinate properties as necessary
var newObject = {};
keys.forEach(function(key) {
newObject[key] = sort(object[key]);
});
return newObject;
}
Live Example (I also updated the fiddle):
function sort(object) {
// Don't try to sort things that aren't objects
if (typeof object != "object") {
return object;
}
// Don't sort arrays, but do sort their contents
if (Array.isArray(object)) {
object.forEach(function(entry, index) {
object[index] = sort(entry);
});
return object;
}
// Sort the keys
var keys = Object.keys(object);
keys.sort(function (a, b) {
var atype = typeof object[a],
btype = typeof object[b],
rv;
if (atype !== btype && (atype === "object" || btype === "object")) {
// Non-objects before objects
rv = atype === 'object' ? 1 : -1;
} else {
// Alphabetical within categories
rv = a.localeCompare(b);
}
return rv;
});
// Create new object in the new order, sorting
// its subordinate properties as necessary
var newObject = {};
keys.forEach(function(key) {
newObject[key] = sort(object[key]);
});
return newObject;
}
var object = {
family: [{
home: {
city: 'Madrid'
},
birth: {
city: 'Madrid'
},
name: 'John',
age: 32
}, {
home: {
city: 'London'
},
birth: {
city: 'Paris'
},
name: 'Marie',
age: 25
}],
name: 'Dani',
age: 33
};
var sortedObject = sort(object);
document.getElementById('container').innerHTML = JSON.stringify(sortedObject, null, '\t');
<pre id="container">
</pre>
(You didn't ask for alphabetical within categories, but it seemed a reasonable thing to throw in.)
That works for me on current Chrome, Firefox, and IE11.

I know I can't relay on javascript properties order, but I need visually to show sorted JSON. Solved in this way:
http://jsfiddle.net/u01mn2py/4/
function sort(object){
if (typeof object != "object" ) // Not to sort the array
return object;
var keys = Object.keys(object);
keys.sort(function(a,b){
if (typeof(object[a])!== 'object') { return -1 } else { return 1 }
});
if( Object.prototype.toString.call( object ) === '[object Array]' ) {
var newObject = [];
} else {
var newObject = {}; }
for (var i = 0; i < keys.length; i++){
newObject[keys[i]] = sort(object[keys[i]])
}
return newObject;
}

Related

How to compare JavaScript objects obtained by parsing JSON?

wtf1 = JSON.parse('{"asdf": "jkl"}');
wtf2 = JSON.parse('{"asdf": "jkl"}');
wtf1 == wtf2; // false
wtf1 === wtf2; // false
I’ve recently stumbled upon the above problem. This counter-intuitive situation makes it hard to, for example, find a specific object in an array deep in a JSON hierarchy.
Any way to somehow compare such objects?
For simple objects you can stringify them again (with ordered keys) and compare strings. For example:
var wtf1 = JSON.parse('{"a": "a", "b": "b"}');
var wtf2 = JSON.parse('{"b": "b", "a": "a"}');
var s1 = JSON.stringify(wtf1, Object.keys(wtf1).sort());
var s2 = JSON.stringify(wtf2, Object.keys(wtf2).sort());
console.log('wtf1 is equal to wtf2: ', (s1 == s2));
For nested objects with prototypes etc you should probably use _.isEqual or any other lib that provides deep equality test.
Note, that in general it's not trivial to correctly implement deep equality test for objects, it's not as simple as iterating and comparing keys/values. However, since the "objects were obtained by parsing JSON" you can skip most of complications and recursively stringify nested values.
I think it's better to just compare them before you parse into objects. But this only works if they are exactly the same, including the order of the properties
var obj1 = {name: "potato", age: 10}
var obj2 = {name: "potato", age: 10}
console.log(obj1 == obj2) //false
console.log(JSON.stringify(obj1) == JSON.stringify(obj2)) //true
var obj1 = {name: "potato", age: 10}
var obj2 = {age: 10, name: "potato"}
console.log(obj1 == obj2) //false
console.log(JSON.stringify(obj1) == JSON.stringify(obj2)) //also false
You cannot compare objects ( as theyre differnet ), but you can iterate over each property, and compare them (and recursively check if theyre objects again):
function compare(obj1,obj2){
//check for obj2 overlapping props
if(!Object.keys(obj2).every(key=>obj1.hasOwnProperty(key))){
return false;
}
//check every key for being same
return Object.keys(obj1).every(function(key){
//if object
if((typeof obj1[key]=="object" )&&( typeof obj2[key]=="object")){
//recursively check
return compare(obj1[key],obj2[key]);
}else{
//do the normal compare
return obj1[key]===obj2[key];
}
});
}
http://jsbin.com/zagecigawi/edit?js
you can use loadash for the same.
using _.isEqual("object1", "object2");
var obj1 = {"prop1" : 2, "prop2" : 3 };
var obj2 = {"prop1" : 2, "prop2" : 3};
console.dir(_.isEqual(obj1, obj2))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
Second approach would be
JSON.stringify(obj1) === JSON.stringify(obj2)
This will compare JSON structure of response and model(It will compare keys only and not values & will work for any hierarchy)
function compareObjects(response, model) {
if (response == "" && model == "") {
return false;
}
if (typeof (response) != "object" && typeof (model) != "object") {
var response = JSON.parse(response);
var model = JSON.parse(model);
}
if (typeof (response) != typeof (model)) {
return false;
} else {
switch (Object.prototype.toString.call(model)) {
case '[object]':
var x;
var mKeys = Object.keys(model);
for (x in mKeys) {
return compareObjects(Object.keys(model)[x], Object.keys(response)[x]);
}
break;
case '[object Array]':
return compareObjects(model[0], response[0]);
case "[object String]":
return model == response;
default:
return true;
}
}
}
var response = '[{"educationId":5,"degreeName":"Bacheltecture - B.Arch"},{"educationId":2,"degreeName":"Bachelor of Arts - B.A. "},{"educationId":3,"degreeName":"Bachelor of Ayurvedic Medicine Surgery - B.A.M.S. "}]';
var model = '[{"degreeName":null},{"educationId":null,"degreeName":null},{"educationId":null,"degreeName":null}]';
var output = compareObjects(response, model);
console.log(output);
Two objects in JS even with same properties are never equal. You can stringify objects and compare those strings or iterate over all properties (but it's hard if you need deep compare)
I recommend using a library implementation for Object equality checking. Try the lodash version.
var object = { 'a': 1 };
var other = { 'a': 1 };
_.isEqual(object, other);
// => true

Get array of key-values from array of objects without knowing format of array of objects (Javascript)?

Imagine I'm given a reference to an array of similar objects, e.g. array will be the name of that array. Now I'm asked to create an array of all the values of some property that is found inside each object of that array, e.g. "user.id".
The problem is that I won't know the format of each object and where that property will reside/be nested.So "user.id" might reside in array[#].someKey (array[#].someKey["user.id"])or in array[#].someKey.someOtherKey (array[#].someKey.someOtherKey["user.id"])
Is there a function (jQuery, underscore..etc) that could create such an array ?
e.g. var arrayOfUserIds = returnArray(array, "user.id");
For example, imagine that the following is an example of such an array :
var array = [
{
"age": "13",
"type": "publish_action",
"tag": null,
"timestamp": 1398931707000,
"content": {
"action": "publish",
"user.id": "860",
"user.email": "alex#somemail.com",
"property.id": "2149",
"iteration_id": "15427",
"test_id": "6063",
"property.name" : "bloop"
}, {
....
}, {
....
}];
Based on the above, I could obviously do :
var arrayOfUserIds = [];
for (var i=0; i<array.length; i++)
{
arrayOfUserIds.push(array[i]["content"]["user.id"]);
}
But like I said, in my case I won't know about the format of the object so I can't create such a for-loop for example.
Any ideas will be much appreciated!
Thank you!
If I understand correctly, each object in someArray either contains a property user.id or an object that contains user.id...or, recursively, some object that contains someArray. You want to create an array containing only the user.id properties.
An easy way to do this would be to do a recursive examination of each object in the array until user.id is located:
// get `user.id` property from an object, or sub-object
// it is assumed that there will only be one such property;
// if there are more than one, only the first one will be returned
function getUserId(o){
if(o===null || o===undefined) return;
if(o['user.id']) return o['user.id'];
for(var p in o){
if(!o.hasOwnProperty(p)) continue;
if(typeof o[p] !== 'object') continue;
if(o[p] === null || o[p] === undefined) continue;
var id = getUserId(o[p]);
if(id) return id;
}
}
function getUserIds(arr){
return arr.map(function(e){
return getUserId(e);
});
}
If you want something a little more generic, you could write a "find" method that will find all instances of a named property in an object tree:
var find = (function(){
function find(matches, o, prop, checkPrototypeChain){
if(typeof o[prop] !== 'undefined') matches.push(o[prop]);
for(var p in o){
if(checkPrototypeChain || !o.hasOwnProperty(p)) continue;
if(typeof o[p] !== 'object') continue;
if(o[p] === null || o[p] === undefined) continue;
find(matches, o[p], prop, checkPrototypeChain);
}
}
return function(o, prop, checkPrototypeChain){
var matches = [];
find(matches, o, prop, checkPrototypeChain);
return matches;
}
})();
Then you could just map your array based on that:
var userIds = someArray.map(function(e){ return find(e, 'user.id'); });
Note that I'm glossing over properties that may be in the prototype chain, but in the find function, I added the ability to additionally search for properties in the prototype chain.
I went with the assumption that you're only working with primitives and object/array literals. In which case, the following method (using underscore) seems to do the trick.
var testSubject = {
mykey: 9,
firstArray: [
{something: 9, another: {x: 'hello', mykey: 'dude'}, mykey: 'whatever'},
{something: 9, another: {x: 'hello', mykey: 'dude2'}, mykey: 'whatever2'},
{
someArray: [
{seven: 7, mykey: 'another'},
{hasNo: 'mykey', atAll: 'mykey'}
]
}
],
anObject: {beef: 'jerky', mykey: 19}
};
function getValuesForKey(subject, searchKey) {
return _.reduce(subject, function(memo, value, key) {
if (_.isObject(value)) {
memo = memo.concat(getValuesForKey(value, searchKey));
} else if (key === searchKey) {
memo.push(value);
}
return memo;
}, []);
}
console.log(getValuesForKey(testSubject, 'mykey'));
// -> [9, "dude", "whatever", "dude2", "whatever2", "another", 19]
It only returns the list of values because they will all share the same key (i.e. the one specified). Additionally, I do believe any matching keys will be ignored if their values are not primitive (e.g. mykey: {…} or mykey: […] should be ignored). I hope it helps.

How do I write a recursive function in Javascript to add up all of the string values of a deeply nested object?

Say I have this object:
{
"prop1":"Hello",
"prop2":{
"prop1":{
"prop1":"Tablecloth",
"prop2":"Indians"
},
"prop2":"JuicyJuice"
},
"prop3":"Sponge",
"prop4":{"Bob":"Squarepants"}
}
I would like a recursive function that will return HelloTableclothIndiansJuicyJuiceSpongeSquarepants.
Whatever object I put it, I want it to cycle though until it gets all of the strings and adds them all up.
Thank you!
Here's a very simple implementation that should work for simple objects like this:
var walkProps = function(obj) {
var s = "";
for(var x in obj)
{
if(typeof obj[x] === "string")
s += obj[x];
else
s += walkProps(obj[x]);
}
return s;
}
Demonstration
Note, though, that that depends on the order in which for-in visits the properties on the object, which is not specified and can vary by engine and by how the object is constructed (for instance, the order in which the properties were added).
Update: With some slight modification, this can be used to return the values based on the alphabetical order of the keys. This method is not sensitive to implementation-dependent ordering of properties:
var walkProps = function(obj) {
var s = "", i = 0, keys = Object.keys(obj).sort(), i;
for(; i < keys.length; i++)
{
if(typeof obj[keys[i]] === "string")
s += obj[keys[i]];
else
s += walkProps(obj[keys[i]]);
}
return s;
}
So even if "prop3" comes before "prop2" it will still return the same output.
Demonstration
You would need to write a function that loops over an object's properties and see if they are a string, and then append the strings to an output. If the property is an object rather than a string, you would want to call the function on this object and append it's return value to your total output.
You can loop over an object's properties using for...in like:
var MyObject = {
'a': 'string1',
'b': 'string2'
};
for (var key in MyObject) {
var value = MyObject[key];
}
To check if a property is a string you would want to do:
typeof value === "string"
Which will return true/false accordingly.
As mentioned, for( var b in a ) may not preserve ordering:
// Return array of string values
function getStrings(a) {
if( typeof(a) == "string" ) return [a];
var list = [];
for( var b in a ) list = list.concat(getStrings(a[b]));
return list;
}
Applied to OP's data:
var a = {
"prop1":"Hello",
"prop2":{
"prop1":{
"prop1":"Tablecloth",
"prop2":"Indians"
},
"prop2":"JuicyJuice"
},
"prop3":"Sponge",
"prop4":{"Bob":"Squarepants"}
}
getStrings(a).join(); // "Hello,Tablecloth,Indians,JuicyJuice,Sponge,Squarepants"
// Or as asked for by OP (again, order is not guaranteed)
getStrings(a).join(''); // "HelloTableclothIndiansJuicyJuiceSpongeSquarepants"

comparing and pushing array of objects in javascript

array1:
[{"Attribute1":"Apple","Attribute2":"jacob.nelson#cognizant.com"}]
array2:
[{"Attribute1":"orange"}]`
I want to replace the value of "Attribute1" in array1 to value of "Attribute1" in array2.
My output should be like
[{"Attribute1":"orange","Attribute2":"jacob.nelson#cognizant.com"}]
I'm new to javascript.Am stuck here.Any help will be much appreciated.
What you showed us is JSON objects representation.
In this case you have an array of objects, so if you do next:
>>ar=[{"Attribute1":"Apple","Attribute2":"jacob.nelson#cognizant.com"}]
[Object]
This says that you have one object in an array, then yoou have to get it:
>>obj=ar[0]
Object {Attribute1: "Apple", Attribute2: "jacob.nelson#cognizant.com"}
Then if you need to replace something in objects, you have to treat them like OBJECTS!
>>ar2=[{"Attribute1":"orange"}]
>>obj2=ar2[0]
>>obj1.Attribute1=obj2.Attribute1
And that's all!
TIP if you have many objects, loop over them:
>>objects_array=[
{"Attribute1":"Apple","Attribute2":"jacob.nelson#cognizant.com"},
{"Attribute1":"Cucumber","Attribute2":"asd#qwe.com"}
]
[Object, Object]
>>for obj in objects_array {
obj.Attribute1='Whatever'
}
This is probably overkill for this single case, however here it is:
Using Object.extend
// adds Object.extend if it does not already exist
if (typeof Object.extend !== 'function') {
Object.extend = function (d /* DESTINATION */, s /* SOURCE */) {
for (var k in s) {
if (s.hasOwnProperty(k)) {
var v = s[k];
if (d.hasOwnProperty(k) && typeof d[k] === "object" && typeof v === "object") {
Object.extend(d[k], v);
} else {
d[k] = v;
}
}
}
return d;
};
}
you can get the desired results by doing this:
var arr1 = [{"Attribute1":"Apple","Attribute2":"jacob.nelson#cognizant.com"}],
arr2 = [{"Attribute1":"orange"}];
arr1 = Object.extend(arr1, arr2);
>> [{"Attribute1":"orange","Attribute2":"jacob.nelson#cognizant.com"}]
But like it is mentioned in the comments; if this is the only case, do this by hand.

javascript find parent of array item

I have an array item like this:
var array = USA.NY[2];
// gives "Albany"
{"USA" : {
"NY" : ["New York City", "Long Island", "Albany"]
}}
I want to find the state from just having the array. How do I do this? Thanks.
function findParent(array) {
// do something
// return NY
}
In Javascript, array elements have no reference to the array(s) containing them.
To achieve this, you will have to have a reference to the 'root' array, which will depend on your data model.
Assuming USA is accessible, and contains only arrays, you could do this:
function findParent(item) {
var member, i, array;
for (member in USA) {
if (USA.hasOwnProperty(member) && typeof USA[member] === 'object' && USA[member] instanceof Array) {
array = USA[member];
for(i = 0; i < array.length; i += 1) {
if (array[i] === item) {
return array;
}
}
}
}
}
Note that I’ve renamed the array parameter to item since you’re passing along a value (and array item), and you expect the array to be returned.
If you want to know the name of the array, you should return member instead.
Here is a generic function that can be used to find the parent key of any kind of multi-dimentional object. I use underscore.js by habit and for conciseness to abstract array vs associative array loops.
(function (root, struct) {
var parent = null;
var check = function (root, struct) {
_.each(root, function (value, key) {
if (value == struct) {
parent = key;
} else if (root == struct) {
parent = '_root';
} else if (typeof value === 'object') {
check(value, struct);
}
});
}
check(root, struct);
return parent;
})

Categories

Resources