Keep the sort order - javascript

I have a object with that values :
category_list = {
"1000":{
"name":"Cars",
"order":"1",
"level": "2"
},
"2010":{
"name":"Houses",
"order":"2",
"level": "2"
},
"1030":{
"name":"Cars",
"order":"3",
"level": "2"
}
}
And when I would like to show it Chrome reorders it based on the Index :
It becomes :
category_list = {
"1000":{
"name":"Cars",
"order":"1",
"level": "2"
},
"1030":{
"name":"Cars",
"order":"3",
"level": "2"
},
"2010":{
"name":"Houses",
"order":"2",
"level": "2"
}
}
I wish to keep the order as it was when pushing! or reorder based on field "order"
Can someone please help with that?

JavaScript objects are by definition unordered.
If you need an ordered list, you should use an array (of objects) instead, e.g.:
var objs = [
{
"key": 1000,
"name":"Cars",
"order": 1,
"level": 2
}, ...
];
objs.sort(function(a, b) {
return a.order - b.order;
});
NB: for numeric properties use numeric types.

JavaScript objects do not guarantee a specific order for their attributes. So the structure you'd like to have simply doesn't exist in JavaScript.
So with the native structures you can get either:
Array: Guaranteed order, but only accessing elements sequentially or by a numeric (0..n-1) index
Object: Arbitrary order, but you can access elements sequentially (again, arbitrary order) or using its key (which can be any string)
If you need both you either need to add an array that maps the order to the object keys, e.g. [1000, 2010, 1030] or store the data in an array and create a mapping like this: {1000: 0, 2010: 1, 1030: 2}.

Related

Deep picking using Underscore.JS

I am trying to use underscoreJs to manipulate a JavaScript object and having problems doing so.
Here is my example
var data = {
"label": "SomeName",
"parent": [{
"id": "parentId",
"resources": [{
"name": "ID1NAME",
"calls": [
"user_get", "user2_post", "user3_delete"
]
}, {
"name": "ID2",
"calls": [
"employee1_get", "employee2_delete", "employee3_update"
]
}]
}]
};
var res = _(data).chain().
pluck('parent').
flatten().
findWhere(function(item){
item === "user_get"
}).
value();
console.log(res);
Using an element which is a part of data.parent.calls[] (example : "user_get") I would like to extract its parent object, i.e. data.parent[0].
I tried above but always get undefined. I appreciate any help on this.
One of the problems you're having is your use of _.pluck. If you execute _.pluck over an object, it'll go over the keys of the object trying to retrieve the property you specified as the second argument (in this case, 'parent'). 'label' is a string and 'parent' is an array so thus the array that you get as a result is [undefined, undefined]. The rest will then go wrong.
One solution could be as follows:
function findCallIndexInParent(call, parent) {
return _.chain(parent)
.pluck('resources')
.flatten()
.findIndex(function (obj) {
return _.contains(obj.calls, call);
})
.value();
}
function findCall(call, data) {
var parent = data.parent;
return parent[findCallIndexInParent(call, parent)];
}
console.log(findCall('user_get', data));
findCall is just a convenient method that will pass the parent property of data to findCallIndexInParent (that will retrieve the index where call is) and return the desired object with the parent array.
Lodash (a fork of underscore) provides a method to get the property of an object that would have come really handy in here (sadly, underscore doesn't have it).
The explanation of findCallIndexInParent is as follows:
Chain the parent list
pluck the resources array
As pluck maps, it returns a list of lists so a flatten is needed.
Find the index of the element which calls contains call
Return the value (the index) of the object that contains call within parent.
Here's the fiddle. Hope it helps.
This would seem to do the trick.
function findByCall(data, call) {
return _.find(data.parent, function(parent) { //From data.parent list, find an item that
return _.some(parent.resources, function(resource) {//has such parent.resource that it
return _.includes(resource.calls, call); //includes the searched resource.calls item
});
});
}
//Test
var data = {
"label": "SomeName",
"parent": [{
"id": "parentId",
"resources": [{
"name": "ID1NAME",
"calls": [
"user_get", "user2_post", "user3_delete"
]
}, {
"name": "ID2",
"calls": [
"employee1_get", "employee2_delete", "employee3_update"
]
}]
}]
};
console.log(findByCall(data, 'user_get'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore.js"></script>
If I understand correctly, you want to get the index of the element in the parent array which has any resource with the specified call.
data = {
"label": "SomeName",
"parent": [{
"id": "parentId",
"resources": [{
"name": "ID1NAME",
"calls": [
"user_get", "user2_post", "user3_delete"
]
}, {
"name": "ID2",
"calls": [
"employee1_get", "employee2_delete", "employee3_update"
]
}]
}]
}
// find the index of a parent
const index = _.findIndex(data.parent, parent =>
// that has any (some) resources
_.some(parent.resources, resource =>
// that contains 'user_get' call in its calls list
_.contains(resource.calls, 'user_get')
)
)
console.log(index) // 0
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
If you want to find the actual parent object, use find instead of findIndex
If you want to find all parent objects matching this call, use filter instead of findIndex

Get JSON keys in order

I have the below in a file and read as
var input = require("./mydata.json");
"User": {
"properties": {
"firstName": {
"type": "string",
"minLength": 1,
"maxLength": 50
},
"lastName": {
"type": "string",
"maxLength": 50
},
"middleName": {
"type": "string"
},
"title": {
"type": "string"
},
"language": {
"type": "string",
"default": "en-US"
}
}
}
I am using the below code to loop through the keys
var item = _.get(input, 'User');
var properties = item.properties;
var allKeys = _.keys(properties);
_.each(allKeys, function(key) {
});
Inside the each loop, I get the firstname, lastname etc, in the same sequence as in the input file.
I want to know if I will get it in order always?
Properties order in objects is not guaranteed in JavaScript; you need to use an Array to preserve it.
Definition of an Object from ECMAScript Third Edition (pdf):
4.3.3 Object
An object is a member of the
type Object. It is an unordered collection of properties each of which
contains a primitive value, object, or
function. A function stored in a
property of an object is called a
method.
Since ECMAScript 2015, using the Map object could be an alternative. A Map shares some similarities with an Object and guarantees the keys order:
A Map iterates its elements in insertion order, whereas iteration order is not specified for Objects.
Yes, If you want to change the order then you will need to sort or arrange the keys in a separate array and then loop on rearranged array.
Object.keys(input.User).sort();
// Will return all keys in sorted array.
Then , if you want to read value of each key following that order , just loop through that array :
Object.keys(input.User).sort().forEach((key,index)=>{
console.log('key :'+key+' , value :'+input.User[key]);
})

JSON list optimization

I want to create a JSON API that returns a list of objects. Each object has an id, a name and some other information. API is consumed using JavaScript.
The natural options for my JSON output seems to be:
"myList": [
{
"id": 1,
"name": "object1",
"details": {}
},
{
"id": 2,
"name": "object2",
"details": {}
},
{
"id": 3,
"name": "object3",
"details": {}
},
]
Now let's imagine that I use my API to get all the objects but want to first do something with id2 then something else with id1 and id3.
Then I may be interested to be able to directly get the object for a specific id:
"myList": {
"1": {
"name": "object1",
"details": {}
},
"2": {
"name": "object2",
"details": {}
},
"3": {
"name": "object3",
"details": {}
},
}
This second option may be less natural when somewhere else in the code I want to simply loop through all the elements.
Is there a good practice for these use cases when the API is used for both looping through all elements and sometime using specific elements only (without doing a dedicated call for each element)?
In your example you've changed the ID value from 1 to id1. This would make operating on the data a bit annoying, because you have to add and remove id all the time.
If you didn't do that, and you were relying on the sorted order of the object, you may be in for a surprise, depending on JS engine:
var source = JSON.stringify({z: "first", a: "second", 0: "third"});
var parsed = JSON.parse(source);
console.log(Object.keys(parsed));
// ["0", "z", "a"]
My experience is to work with arrays on the transport layer and index the data (i.e. convert array to map) when required.

Keep order of objects inside a JSON String after they are parsed

I receive the following JSON string from an API function.
"Inbound": {
"callRelatedFields": ["ANI",
"DNIS"],
"objects": {
"Contact": [{
"displayName": "Name",
"apiName": "Name"
},
{
"displayName": "Email",
"apiName": "Email"
}],
"Account": [{
"displayName": "Account Name",
"apiName": "Name"
},
{
"displayName": "Phone",
"apiName": "Phone"
},
{
"displayName": "Fax",
"apiName": "Fax"
}],
"cnx__Phone__c": [{
"displayName": "Phone Name",
"apiName": "Name"
},
{
"displayName": "Phone Number Line 1",
"apiName": "cnx__Phone_Number_Line_1__c"
},
{
"displayName": "Phone Number Line 2",
"apiName": "cnx__Phone_Number_Line_2__c"
},
{
"displayName": "Type",
"apiName": "cnx__Type__c"
},
{
"displayName": "Location",
"apiName": "cnx__Location__c"
},
{
"displayName": "Call Manager",
"apiName": "cnx__Call_Manager__c"
},
{
"displayName": "Mac Address",
"apiName": "cnx__Mac_Address__c"
}]
},
"screenPopSettings": {
"screenPopsOpenWithin": "ExistingWindow",
"SingleMatch": {
"screenPopType": "PopToEntity"
},
"NoMatch": {
"screenPopType": "DoNotPop"
},
"MultipleMatches": {
"screenPopType": "DoNotPop"
}
}
}
The order of the objects inside "objects" is important!
But when i parse this JSON string with JSON.parse, the order of those objects is lost.
Is there any good way to keep the order of those objects after they are parsed.
I tried to manipulate the string and convert the whole "objects" into an array, but this turned out to become way too complicated and hacky.
I have a suspicion that the thing that makes you think the keys have changed order is that Chrome devtools show objects with their keys sorted in alphabetical order. Whereas if you use Object.keys() or the equivalent JS to manually iterate through the keys, you will find they come out in the order they were defined in the JSON string.
Here is the equivalent JS for Object.keys():
function objectKeys(obj) {
var keys = [];
if (!obj) return keys;
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
keys.push(key);
}
}
}
When I call this with the objects part of the parsed object I get the following array:
["Contact", "Account", "cnx__Phone__c"]
Unfortunately object properties are unordered in JavaScript so you shouldn't rely on being able to iterate over them in a particular sequence.
I would suggest accessing the properties by name in the order you need them, rather than just iterating over the list.
As per the JSON standard, an object is unordered. So if you care about the order "Contact", "Account", "cnx__Phone__c", put them in an array ([]).
Maybe it's enough to put the property names themselves in an array next to the .objects themselves, so that you still can access them by their names. Many structures are valid solutions.
This solution works only if the properties and the data does not contain one of these characters: {, } and :.
Maybe you replace the curly brackets to square brackets and ": to #",. After that, you can the JSON string parse and get all objects replaced by arrays. The reading is: first value is the property (marked with # at the end) and the second value is the value.
The replacement machanism shuld be improved, in particular the replacement of ":, which can sometimes be wrong, and the search of the curly brackets.
var json = '{"Inbound":{"callRelatedFields":["ANI","DNIS"],"objects":{"Contact":[{"displayName":"Name","apiName":"Name"},{"displayName":"Email","apiName":"Email"}],"Account":[{"displayName":"Account Name","apiName":"Name"},{"displayName":"Phone","apiName":"Phone"},{"displayName":"Fax","apiName":"Fax"}],"cnx__Phone__c":[{"displayName":"Phone Name","apiName":"Name"},{"displayName":"Phone Number Line 1","apiName":"cnx__Phone_Number_Line_1__c"},{"displayName":"Phone Number Line 2","apiName":"cnx__Phone_Number_Line_2__c"},{"displayName":"Type","apiName":"cnx__Type__c"},{"displayName":"Location","apiName":"cnx__Location__c"},{"displayName":"Call Manager","apiName":"cnx__Call_Manager__c"},{"displayName":"Mac Address","apiName":"cnx__Mac_Address__c"}]},"screenPopSettings":{"screenPopsOpenWithin":"ExistingWindow","SingleMatch":{"screenPopType":"PopToEntity"},"NoMatch":{"screenPopType":"DoNotPop"},"MultipleMatches":{"screenPopType":"DoNotPop"}}}}';
json = json.replace(/{/g, '[').replace(/}/g, ']').replace(/"\:/g, '#",');
json = JSON.parse(json);
document.write('<pre>' + JSON.stringify(json, 0, 4) + '</pre>');
#GregL is right the JSON parsed came in alphabetic or in case of a number in ascending order and to keep the order you'll need an incremented number logic like:
var position_in_array = 0
var name = 'screenPopSettings'
object[`${position_in_array}${name}`] = value
position_in_array += 1
The parseJson returns data in object form and object doesn't has index. So we should define custom index of data array, if we want to keep the array index.
Example:
$arr[0] = array(
'Contact'=>array(
'key1'=>'val',
)
);
$arr[1] = array(
'Account'=>array(
'key1'=>'val',
)
);
It will produce the output as per the array index originally defined before parseJson function call.

How to sort an array of objects in ascending order of number?

I have an array of objects like the following :
var array = {
"112" : {
"id": "3",
"name": "raj"
},
"334" : {
"id": "2",
"name": "john"
},
"222" : {
"id": "5",
"name": "kelvin"
}
}
Now i want to sort the array in ascending order of id and then restore it in array. I tried using sort() but could not do it. Please help how to do so that when i display the data from the array it comes sorted.
Assuming you meant your code to be an array of objects, ie:
var unsortedArray = [
{ id: 3, name: "raj" },
{ id: 2, name: "john" },
{ id: 5, name: "kelvin" }
];
Then you would be able to sort by id by passing a function to Array.sort() that compares id's:
var sortedArray = unsortedArray.sort(function(a, b) {
return a.id - b.id
});
As others have pointed out, what you have is an object containing objects, not an array.
var array = {
"112" : {
"id": "3",
"name": "raj"
},
"334" : {
"id": "2",
"name": "john"
},
"222" : {
"id": "5",
"name": "kelvin"
}
}
var sortedObject = Array.prototype.sort.apply(array);
result:
{
"112": {
"id": "3",
"name": "raj"
},
"222": {
"id": "5",
"name": "kelvin"
},
"334": {
"id": "2",
"name": "john"
}
}
That isn't an array, it is an object (or would it if it wasn't for the syntax errors (= should be :)). It doesn't have an order.
You could use an array instead (making the current property names a value of a key on the subobjects).
Alternatively, you could use a for loop to build an array of the key names, then sort that and use it as a basis for accessing the object in order.
JavaScript objects are unordered by definition. The language specification doesn't even guarantee that, if you iterate over the properties of an object twice in succession, they'll come out in the same order the second time.
If you need things to be ordered, use an array and the Array.prototype.sort method.
That is an object but you can sort an array ilke this:
Working Demo: http://jsfiddle.net/BF8LV/2/
Hope this help,
code
function sortAscending(data_A, data_B)
{
return (data_A - data_B);
}
var array =[ 9, 10, 21, 46, 19, 11]
array.sort(sortAscending)
alert(array);​
Not many people knows that Array.sort can be used on other kinds of objects, but they must have a length property:
array.length = 334;
Array.prototype.sort.call(array, function(a, b) {return a.id - b.id;});
Unfortunately, this doesn't work well if your "array" is full of "holes" like yours.

Categories

Resources