sort json object in javascript - javascript

For example with have this code:
var json = {
"user1" : {
"id" : 3
},
"user2" : {
"id" : 6
},
"user3" : {
"id" : 1
}
}
How can I sort this json to be like this -
var json = {
"user3" : {
"id" : 1
},
"user1" : {
"id" : 3
},
"user2" : {
"id" : 6
}
}
I sorted the users with the IDs..
I don't know how to do this in javascript..

First off, that's not JSON. It's a JavaScript object literal. JSON is a string representation of data, that just so happens to very closely resemble JavaScript syntax.
Second, you have an object. They are unsorted. The order of the elements cannot be guaranteed. If you want guaranteed order, you need to use an array. This will require you to change your data structure.
One option might be to make your data look like this:
var json = [{
"name": "user1",
"id": 3
}, {
"name": "user2",
"id": 6
}, {
"name": "user3",
"id": 1
}];
Now you have an array of objects, and we can sort it.
json.sort(function(a, b){
return a.id - b.id;
});
The resulting array will look like:
[{
"name": "user3",
"id" : 1
}, {
"name": "user1",
"id" : 3
}, {
"name": "user2",
"id" : 6
}];

Here is a simple snippet that sorts a javascript representation of a Json.
function isObject(v) {
return '[object Object]' === Object.prototype.toString.call(v);
};
JSON.sort = function(o) {
if (Array.isArray(o)) {
return o.sort().map(JSON.sort);
} else if (isObject(o)) {
return Object
.keys(o)
.sort()
.reduce(function(a, k) {
a[k] = JSON.sort(o[k]);
return a;
}, {});
}
return o;
}
It can be used as follows:
JSON.sort({
c: {
c3: null,
c1: undefined,
c2: [3, 2, 1, 0],
},
a: 0,
b: 'Fun'
});
That will output:
{
a: 0,
b: 'Fun',
c: {
c2: [3, 2, 1, 0],
c3: null
}
}

In some ways, your question seems very legitimate, but I still might label it an XY problem. I'm guessing the end result is that you want to display the sorted values in some way? As Bergi said in the comments, you can never quite rely on Javascript objects ( {i_am: "an_object"} ) to show their properties in any particular order.
For the displaying order, I might suggest you take each key of the object (ie, i_am) and sort them into an ordered array. Then, use that array when retrieving elements of your object to display. Pseudocode:
var keys = [...]
var sortedKeys = [...]
for (var i = 0; i < sortedKeys.length; i++) {
var key = sortedKeys[i];
addObjectToTable(json[key]);
}

if(JSON.stringify(Object.keys(pcOrGroup).sort()) === JSON.stringify(Object.keys(orGroup)).sort())
{
return true;
}

Related

Determine if array element represents an object or a value

From a JSON object (containing stock data), I want to add certain elements to an array (in Google Sheets script editor):
var quote = JSON.parse(response.getContentText());
// Example of what quote object looks like:
{
"quoteSummary": {
"result": [
{
"Profile": {
"sector": "Technology",
"website": "www.test.com"
},
"Owners": [
{
"name": "Eric",
"age": "28"
},
{
"name": "Susan",
"age": "44"
}
],
"Profit": 100,
"Assets": 7000
}
]
}
}
Here is my current approach to read only some specific values:
var arr = [];
arr.push(quote.quoteSummary.result[0].Profile.sector); // Technology
arr.push(quote.quoteSummary.result[0].Owners[1].name); // Susan
arr.push(quote.quoteSummary.result[0].Profit); // 100
But since there are many specific properties to read, I'd like to use a loop:
var quote = JSON.parse(response.getContentText());
var arr = [];
var el = [
['Profile', 'sector'],
['Owners[1]', 'name'],
['Profit']
];
for (i = 0; i < el.length; i++)
{
if (quote.quoteSummary.result[0][el[i][0]][el[i][1]] !== undefined)
{
arr.push(quote.quoteSummary.result[0][el[i][0]][el[i][1]].value);
}
}
/*
Expected output (if I would loop through arr):
Technology
Susan
100
*/
The point is that different stocks, will have different properties. So el might define some non-existing elements or properties. Assume (in a bit of an other way of defining el -- as I wrote, I'm plexible here.. perhaps the paths are the easiest):
var el = [
'Profile.website',
'Profile.name',
'Assets'
]
/*
Expected output:
www.test.com
<----- "name" doesn't exist!
7000
Notice that in this example, there is no property "name" in Profile,
so I'd like to add an empty element to arr
*/
But this does not work. What is a generic loop that accomplishes what I'm trying to do here? The array defining what I want can also be constructed differently if that helps. But the point is that I don't end up with a script like:
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
I recommend you use variable-length chains of property names. Each name in a given chain represents a deeper property. You can "dive" into an object through an arbitrary number of property names with code like this:
let dive = (obj, propertyNames) => {
for (let pn of propertyNames) obj = obj[pn];
return obj;
};
Now you can say:
let dive = (obj, propertyNames) => {
for (let pn of propertyNames) obj = obj[pn];
return obj;
};
let quote = {
quoteSummary: {
result: [
{
Profile: {
sector: 'Technology',
website: 'www.test.com'
},
Owners: [
{
name: 'Eric',
age: '28'
},
{
name: 'Susan',
age: '44'
}
],
Profit: 100,
Assets: 7000
}
]
}
};
// Here are the "variable-length property chains":
let el = [
[ 'Profile', 'sector' ],
[ 'Owners', 1, 'name' ],
[ 'Profit' ]
];
// Here's how to combine `el`, `dive`, and your `quote` data to get a result:
let arr = el.map(propertyNames => dive(quote.quoteSummary.result[0], propertyNames));
console.log(arr);
You could even replace dive with Array.prototype.reduce, if you'd like to stay functional but avoid the function definition:
dive(someObj, propertyNames);
is equivalent to
propertyNames.reduce((obj, propName) => obj[propName], someObj);
Note the above code all assumes that a property exists for each term in the property chain (except the final property name, which may resolve to undefined without causing any errors). If some cases may have the, e.g., Profile key undefined or null you'll need to write some kind of if (propertyDoesntExist) / else statement which describes how to deal with missing properties.
For example you could modify dive to handle non-existent properties:
let dive = (obj, propertyNames, valueIfMissing=null) => {
for (let pn of propertyNames) {
// Intentional use of loose `==` operator!
if (obj == null) return valueIfMissing;
obj = obj[pn];
}
return obj;
};
This means that:
dive({ a: 1, b: 2, c: 3 }, [ 'd' ]) === null;
But we can substitute any default return value:
dive({ a: 1, b: 2, c: 3 }, [ 'd' ], 'ddd') === 'ddd';
Note this works at any depth:
dive({ a: { a: 1 }, b: { b: 2 }, c: { c: 3 }, d: null }, [ 'd', 'd' ]) === null;
dive({ a: { a: 1 }, b: { b: 2 }, c: { c: 3 }, d: null }, [ 'd', 'd' ], 'ddd') === 'ddd';
You should be able to do this:
if(y.value){
arr.push(y.value);
} else {
arr.push(y);
}
However, this will break if y.value happens to be something like 0, false, etc.
If this is the case you can do this:
if(y.hasOwnProperty("value")){
arr.push(y.value);
} else {
arr.push(y);
}
Based on the comment:
if (quote.quoteSummary.result[0][el[i][0]].hasOwnProperty("value")) {
arr.push(quote.quoteSummary.result[0][el[i][0]].value);
} else {
arr.push(quote.quoteSummary.result[0][el[i][0]]);
}

Access array of object elements in order from object field? [duplicate]

This question already has answers here:
Sorting an array of objects by property values
(35 answers)
Closed 5 years ago.
I have a JS object that has the following snippet:
{
"foo": "bar",
"baz": [
{
"order": 2,
"fruit": "banana"
},
{
"order": 1,
"fruit": "apple"
},
{
"order": 3,
"fruit": "peach"
},
]
}
My goal is to be able to iterate through all the objects in "baz" but in the order according to their "order" property, not the order as stored in the object. The order of the objects in "baz" can/will change frequently, so sorting them previously or storing them in the order they need to be in is not an option.
A pure JS or jQuery answer is all acceptable.
Hello you have to sort your object. Just use .sort of the array for that.
Here is a sample:
var obj = {
"foo": "bar",
"baz": [
{
"order": 2,
"fruit": "banana"
},
{
"order": 1,
"fruit": "apple"
},
{
"order": 3,
"fruit": "peach"
},
]
}
// get property
var arr = obj["baz"];
// may copy array
var counter = arr.length;
var arrCopy = new Array(counter);
while(counter--) {
arrCopy[counter] = arr[counter];
}
// sort
arrCopy.sort(function(a, b) { return a.order - b.order});
// iterate it
arrCopy.forEach(function(v) {
console.log(v.fruit);
})
You've said
The order of the objects in "baz" can/will change frequently...
If you literally mean just that, and not that their order property changes, you can give yourself another array, separate from baz, that contains the objects in order. Then it doesn't matter what order they're in in baz.
var bazInOrder = theObject.baz.slice();
bazInOrder.sort(function(a, b) {
return a.order - b.order;
});
If you add an entry to baz, you'll need to add it (in sorted position) to bazInOrder; and similarly if you delete an entry from baz, you'll want to delete it from bazInOrder.
Your other option is to add a property to them pointing to the "next" entry; e.g., overlaying a linked list on the array. That again requires a fair bit of maintenance on add/remove, but not just if their order in baz changes.
theObject.baz.slice()
.sort(function(a, b) { return a.order - b.order; })
.forEach(function(entry, index, arr) {
entry.next = index < arr.length - 1 ? arr[index + 1] : null;
});
then to access them in order requires finding the order = 1 entry, then looping with next:
for (var e = theObject.baz.find(function(e) { return e.order === 1; });
e;
e = e.next) {
// ...use `e`...
}
Again, though, linked lists involve maintenance on add/remove.
I guess you could create an array that has the original index of baz in order of baz[n].order
a bit like
var obj = {
"foo": "bar",
"baz": [
{
"order": 2,
"fruit": "banana"
},
{
"order": 1,
"fruit": "apple"
},
{
"order": 3,
"fruit": "peach"
},
]
}
var bazIndexOrder = obj.baz
.map(({order}, index) => ({index, order}))
.sort(({order:a}, {order:b}) => a - b)
.map(({index}) => index);
console.log(bazOrder); // [1, 0, 2]
bazOrder.forEach(index => console.log(obj.baz[index].fruit)); // apple, banana, peach
If the ES2015+ code scares you, it's
var bazIndexOrder = obj.baz.map(function (_ref, index) {
var order = _ref.order;
return { index: index, order: order };
}).sort(function (_ref2, _ref3) {
var a = _ref2.order;
var b = _ref3.order;
return a - b;
}).map(function (_ref4) {
var index = _ref4.index;
return index;
});

Remove duplicate object from array javascript [duplicate]

I have this kind of array:
var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];
I'd like to filter it to have:
var bar = [ { "a" : "1" }, { "b" : "2" }];
I tried using _.uniq, but I guess because { "a" : "1" } is not equal to itself, it doesn't work. Is there any way to provide underscore uniq with an overriden equals function?
.uniq/.unique accepts a callback
var list = [{a:1,b:5},{a:1,c:5},{a:2},{a:3},{a:4},{a:3},{a:2}];
var uniqueList = _.uniq(list, function(item, key, a) {
return item.a;
});
// uniqueList = [Object {a=1, b=5}, Object {a=2}, Object {a=3}, Object {a=4}]
Notes:
Callback return value used for comparison
First comparison object with unique return value used as unique
underscorejs.org demonstrates no callback usage
lodash.com shows usage
Another example :
using the callback to extract car makes, colors from a list
If you're looking to remove duplicates based on an id you could do something like this:
var res = [
{id: 1, content: 'heeey'},
{id: 2, content: 'woah'},
{id: 1, content:'foo'},
{id: 1, content: 'heeey'},
];
var uniques = _.map(_.groupBy(res,function(doc){
return doc.id;
}),function(grouped){
return grouped[0];
});
//uniques
//[{id: 1, content: 'heeey'},{id: 2, content: 'woah'}]
Implementation of Shiplu's answer.
var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];
var x = _.uniq( _.collect( foo, function( x ){
return JSON.stringify( x );
}));
console.log( x ); // returns [ { "a" : "1" }, { "b" : "2" } ]
When I have an attribute id, this is my preffered way in underscore:
var x = [{i:2}, {i:2, x:42}, {i:4}, {i:3}];
_.chain(x).indexBy("i").values().value();
// > [{i:2, x:42}, {i:4}, {i:3}]
Using underscore unique lib following is working for me, I m making list unique on the based of _id then returning String value of _id:
var uniqueEntities = _.uniq(entities, function (item, key, a) {
return item._id.toString();
});
Here is a simple solution, which uses a deep object comparison to check for duplicates (without resorting to converting to JSON, which is inefficient and hacky)
var newArr = _.filter(oldArr, function (element, index) {
// tests if the element has a duplicate in the rest of the array
for(index += 1; index < oldArr.length; index += 1) {
if (_.isEqual(element, oldArr[index])) {
return false;
}
}
return true;
});
It filters out all elements if they have a duplicate later in the array - such that the last duplicate element is kept.
The testing for a duplicate uses _.isEqual which performs an optimised deep comparison between the two objects see the underscore isEqual documentation for more info.
edit: updated to use _.filter which is a cleaner approach
The lodash 4.6.1 docs have this as an example for object key equality:
_.uniqWith(objects, _.isEqual);
https://lodash.com/docs#uniqWith
Try iterator function
For example you can return first element
x = [['a',1],['b',2],['a',1]]
_.uniq(x,false,function(i){
return i[0] //'a','b'
})
=> [['a',1],['b',2]]
here's my solution (coffeescript) :
_.mixin
deepUniq: (coll) ->
result = []
remove_first_el_duplicates = (coll2) ->
rest = _.rest(coll2)
first = _.first(coll2)
result.push first
equalsFirst = (el) -> _.isEqual(el,first)
newColl = _.reject rest, equalsFirst
unless _.isEmpty newColl
remove_first_el_duplicates newColl
remove_first_el_duplicates(coll)
result
example:
_.deepUniq([ {a:1,b:12}, [ 2, 1, 2, 1 ], [ 1, 2, 1, 2 ],[ 2, 1, 2, 1 ], {a:1,b:12} ])
//=> [ { a: 1, b: 12 }, [ 2, 1, 2, 1 ], [ 1, 2, 1, 2 ] ]
with underscore i had to use String() in the iteratee function
function isUniq(item) {
return String(item.user);
}
var myUniqArray = _.uniq(myArray, isUniq);
I wanted to solve this simple solution in a straightforward way of writing, with a little bit of a pain of computational expenses... but isn't it a trivial solution with a minimum variable definition, is it?
function uniq(ArrayObjects){
var out = []
ArrayObjects.map(obj => {
if(_.every(out, outobj => !_.isEqual(obj, outobj))) out.push(obj)
})
return out
}
var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];
var bar = _.map(_.groupBy(foo, function (f) {
return JSON.stringify(f);
}), function (gr) {
return gr[0];
}
);
Lets break this down. First lets group the array items by their stringified value
var grouped = _.groupBy(foo, function (f) {
return JSON.stringify(f);
});
grouped looks like:
{
'{ "a" : "1" }' = [ { "a" : "1" } { "a" : "1" } ],
'{ "b" : "2" }' = [ { "b" : "2" } ]
}
Then lets grab the first element from each group
var bar = _.map(grouped, function(gr)
return gr[0];
});
bar looks like:
[ { "a" : "1" }, { "b" : "2" } ]
Put it all together:
var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];
var bar = _.map(_.groupBy(foo, function (f) {
return JSON.stringify(f);
}), function (gr) {
return gr[0];
}
);
You can do it in a shorthand as:
_.uniq(foo, 'a')

Filtering array of Objects Using Lodash or Javascript based on property name

I am Having the Array of objects. Like this
var result=[{"batchId":123, "licenseId":2345ef34, "name":"xxx"},
{"batchId":345, "licenseId":2345sdf334, "name":"www"},
{"batchId":145, "licenseId":234sdf5666, "name":"eee"},
{"batchId":455, "licenseId":asfd236645 },
{"batchId":678, "name":"aaa"}]
i want to have the array which is contains all the three properties. the Output should be like this.
[{"batchId":123, "licenseId":2345ef34, "name":"xxx"},
{"batchId":345, "licenseId":2345sdf334, "name":"www"},
{"batchId":145, "licenseId":234sdf5666, "name":"eee"}]
can anybody Help me on this
This is simple with the array .filter() method:
var result=[
{"batchId":123, "licenseId":"2345ef34", "name":"xxx"},
{"batchId":345, "licenseId":"2345sdf334", "name":"www"},
{"batchId":145, "licenseId":"234sdf5666", "name":"eee"},
{"batchId":455, "licenseId":"asfd236645" },
{"batchId":678, "name":"aaa"}
];
var filtered = result.filter(function(v) {
return "batchId" in v && "licenseId" in v && "name" in v;
});
console.log(filtered);
The function you pass to .filter() is called for each element in the array. Each element for which you return a truthy value will be included in the resulting array.
In the code above I simply test if all three of those specific properties are present, although there are other tests you could use that would get the same result for that data:
var result=[ {"batchId":123, "licenseId":"2345ef34", "name":"xxx"}, {"batchId":345, "licenseId":"2345sdf334", "name":"www"}, {"batchId":145, "licenseId":"234sdf5666", "name":"eee"}, {"batchId":455, "licenseId":"asfd236645" }, {"batchId":678, "name":"aaa"} ];
var filtered = result.filter(function(v) {
return Object.keys(v).length === 3;
});
console.log(filtered);
Note that you need to put your licenseId values in quotes, because they seem to be string values.
var result = [{
"batchId": 123,
"licenseId": '2345ef34',
"name": "xxx"
}, {
"batchId": 345,
"licenseId": '2345sdf334',
"name": "www"
}, {
"batchId": 145,
"licenseId": '234sdf5666',
"name": "eee"
}, {
"batchId": 455,
"licenseId": 'asfd236645'
}, {
"batchId": 678,
"name": "aaa"
}];
function hasProperties(object) {
return object.hasOwnProperty('batchId') && object.hasOwnProperty('licenseId') && object.hasOwnProperty('name')
}
result.filter(e => hasProperties(e));

Sorting array into array new based on string

I'm trying to sort a JSON into multiple arrays based on type, my current json is:
// current json file:
{
"res": [
{
"type" : "stream",
"price" : "3.99",
"id" : "13nee"
},
{
"type" : "stream",
"price" : "2.99",
"id" : "8ejwj"
},
{
"type" : "buy",
"price" : "3.99".
"id" : "9akwk"
},
...
]
}
I'm looking to sort it into multiple arrays by type like below:
var sorted = {
"stream" : [
{
"price" : "2.99",
"id" : "8ejwj"
},
{
"price" : ".99",
"id" : "13nee"
},
... // other objects with type: "stream"
],
"buy" : [
{
"price" : "3.99".
"id" : "9akwk"
},
... // other objects with type: "buy"
]
}
I've tried it, but the only solution I can think of is by cases - run if loop, if case matches type, then push object to array. Is there a more elegant solution?
var items = {};
var i = 0;
for(i; i < res.length; i += 1){
var resItem = res[i];
if(items.hasOwnProperty(resItem.type)){
items[resItem.type].push({price:resItem.price, id:resItem.id});
} else {
items[resItem.type] = [{price:resItem.price, id:resItem.id}];
}
}
The properties on JavaScript objects are hashed, so you can dynamically match and generate new objects like above. If you want to apply a well ordering sort, you'll need to apply it to the arrays of the newly generated items object.
Step 1 :
Convert the JSON to a jquery object :
var x = jQuery.parseJSON( jsonString );
Step 2:
Use underscore library's _.groupBy to group :
_.groupBy(x,'type');
There might be some adjustment you need to do for x being array or object.
Edit :
You don't need step1. Just do :
sorted = _.groupBy(json.res,'type');
You could do something like this with ECMA5. This performs, generically, the sort and reduce that you have indicated in your question, so you can add more fields to your data without having to change the routine. It also leaves your original data intact.
Javascript
var original = {
'res': [{
'type': 'stream',
'price': '3.99',
'id': '13nee'
}, {
'type': 'stream',
'price': '2.99',
'id': '8ejwj'
}, {
'type': 'buy',
'price': '3.99',
'id': '9akwk'
}]
},
sorted = {};
original.res.slice().sort(function (a, b) {
a = +(a.price);
b = +(b.price);
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
}).reduce(function (acc, element) {
if (!acc[element.type]) {
acc[element.type] = [];
}
acc[element.type].push(Object.keys(element).filter(function (name) {
return name !== 'type';
}).reduce(function (prev, name) {
prev[name] = element[name];
return prev;
}, {}));
return acc;
}, sorted);
console.log(JSON.stringify(sorted));
Output
{
"stream": [{
"price": "2.99",
"id": "8ejwj"
}, {
"price": "3.99",
"id": "13nee"
}],
"buy": [{
"price": "3.99",
"id": "9akwk"
}]
}
On jsFiddle

Categories

Resources