Jquery + rails 4
In json_data instance i have some data with key and value, The key is an integer id and the value is an object which contains data.However when I try to iterate over this data with the jQuery $.each function, the results come back sorted by the key instead.How can I iterate over my collection of objects in their original order?
$.each(json_data, function(key, value){
console.log(key);
});
key = 6181 30654 39148 30743 30510 42998 5788 30401 ...//Mozilla Working Fine (Right)
key = 5788 6011 6181 30401 30510 30639 30654 30698 30743 ...// Chrome Not Working Fine (Wrong)
Regarding "order" in an object:
A JavaScript object is a hash table, and is optimized for constant time lookup of key:value pairs.
Arrays are a data structure in which the elements are assigned to a discrete index value. When you iterate over the elements of an array, you will return a predictable pattern that matches the order of the items in the array.
In an object however, there are no index values, so there is no constant predictable way to iterate over it in order. The object just stores key:value pairs that are optimized for constant-time lookup.
EDIT: I will demonstrate those two methods of iterating just for illustration, but I wanted to warn you in advance, they won't change the fact that you won't get the keys returned in a consistent order.
var json_data = {6181:true, 30654:true, 39148:true, 30743:true, 30510:true, 42998:true, 5788:true, 30401:true};
for(item in json_data){
console.log(item);
} // *might* return a different order based on browser or JavaScript implementation
Clarifying one more time: objects are not associated with a particular 'order'. They are optimized to provide "constant time" lookup. Regardless of the size of the object, if you query a key, the associated value will be returned to you in constant time.
If you need to impose a particular order, you will need to use an array.
Example:
var json_data = [6181, 30654, 39148, 30743, 30510, 42998, 5788, 30401];
for(var i = 0; i < json_data.length; i++){
console.log(json_data[i]);
}
// always returns the values in the same order they are in the json_data array.
// changing the order in the array will change the order they are output and
// and that order will be the same regardless of which browser or version of JavaScript you
// are using.
Related
I have a map of id => value that I want to sort by value.
But no matter what I do, it always gets sorted by id.
Basically I have a sorted map on server side that I send to javascript via json.
{"3":"Apple","2":"Banana","1":"Orange"}
After de-serialization I get
{
1:"Orange",
2:"Banana",
3:"Apple"
}
And no matter what I try, it seems to stay in this order. Is it possible in javascript to force a non ascending sort order with interger keys?
var json = '{"3":"Apple", "2":"Banana", "1":"Orange"}';
var data = $.parseJSON(json);
for (var ix in data) {
console.log(ix + ": " + data[ix]);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
You should not rely on objects key order for these reasons.
I personally would recommend you to either use a Map, or to build an Array instead.
Below is an example to build an array from your source: for simplicity, I've added a key property to make the sorting easier.
Note: I'm using Array.from to build the array, which is taking the length from the parsed object keys length, and using the callback to init the object inline.
var json = '{"3":"Apple", "2":"Banana", "1":"Orange"}';
// Parse the json string.
const parsed = JSON.parse(json);
// Acquire the keys length
const length = Object.keys(parsed).length;
// Build an array of objects ordered in the same way it came.
const result = Array.from({length}, (_, i) => ({key: length - i, [length - i]: parsed[length - i]}));
// Log a copy of the result.
console.log(JSON.parse(JSON.stringify(result)));
// Sort ascending:
result.sort((a,b) => a.key - b.key);
// Log a copy of the sorted result.
console.log(JSON.parse(JSON.stringify(result)));
// Sort descending:
result.sort((a,b) => b.key - a.key);
// log the sorted array
console.log(result);
If you really want to rely on key orders, you can (of course), but using an array is slightly cleverer and gives no chance to have something which is not ordered as expected, unless (of course) the sorting algorithm is wrong or fails for some reason (like if key is undefined or null or not numeric in the above case).
As a final note, I'm aware that the question is about sorting an object, but because of the above reasons, I think the correct answer is just to DON'T use an object at all in that scenario specifically.
I have a React application which handles rooms and their statistics.
Previously, I had the code set up to pass as props to the next component:
the raw statistics (not a concern for the question)
an array of all the rooms set up as follows
I figured it would be simpler for me, though, to have the list of all rooms as an associative array where the keys of each element is the same as the ID it contains. To do that, I utilized a code similar to this in a for loop:
roomsList[rooms[v].ID] = rooms[v];
So that the result would be:
[a001: {...}, a002: {...}, ...]
I then proceeded to pass this style of array, and not the standard one with a numeric index, as a prop to the next component as such:
<StatsBreakdown stats={computedStats.current} roomsList={roomsList} />
BUT
Now, the next component sees that prop as an empty array.
Even more weirdly, if I initialize that roomsList array with a random value [0] and then do the same process, I end up with:
I cannot cycle through the array with .map, and, according to JS, the length is actually 0, it's not only Google Chrome.
Is there something I'm missing about the way JSX, JS or React work?
Your original roomsList was an array of objects, whose indices were 0,1,2 etc. roomsList[rooms[v].ID] = rooms[v]; implies you are inserting elements not using a number but an alphanumeric string. Hence your resulting array is no longer an array but an object.
So we can cycle over the object using Object.keys().
const renderRoomDets = Object.keys(roomsList).map(room => {
roomOwner = roomsList[room].owner_id;
return (
<div>
<p>{`Room Owner ${roomOwner}`}</p>
</div>
);
});
But I believe your original form is ideal, because you are reaping no special benefits from this notation.
A better alternative maybe using .find() or .findIndex() if you want iterate over an array based on a specific property.
const matchedRoom = roomsList.find(room => room.ID === 'Srf4323')
Iterate the new array using its keys not indexes.
Or even better store your data in an actual object instead of an array since you're using strings for ids.
First define your object like so:
let data = {};
Then start adding records to it. I'd suggest deleting the ID attribute of the object since you're storing it in the key of your record, it's redundant, and it won't go anywhere unless u delete the entry.
data[ID] = row;
To delete the ID attribute (optional):
row.ID = null;
delete row.ID;
Then iterate through it using
for(let key in data){}
Recently i had to make an Array with values at large indexes (due to plugin constraints).
eg:
var names[100000] = "a";
var names[150000] = "b" ... and so on till 5 large indexes.
and in between all values are undefined names[100001] //undefined.
Now my doubt is Since the array has only 5 elements but if i do
names.length //it is 300001
its a large Array. I am not iterating this array nor i am running any loop through it. I will get the values directly through their defined indexes from the array.
So will this structure make any significant performance differences on the Browser or is it alright to use this as long as the number of values in the array is less irrespective of its indexes and no iteration is involved.
Thanks
The only thing that differentiates an array from a plain object is its length property and how it behaves (and a few array specific methods of course). The length value simply increases with certain operations, like setting a numeric property or pushing a new element. That's it in a nutshell. The array doesn't actually contain 100000 elements when you set the property 100000 to a value, all that's happening is that you're setting one property and the value of length is adjusted accordingly.
So, no, it won't have a lot of impact on performance, unless somebody actually iterates through the array using for (let i = 0; i < arr.length; i++).
You can create an array with the length, given by your plugin and work locally with an object to limit the iterations. After all your processing has been applied, you copy the values to the array and send it to the plugin's function.
Keep an array with the desired length as a buffer
var buffer = new Array(20000);
Internally work with an object
var data = {};
Assign values to the object
data[10001] = "foo";
Once transformations or data processing has been applied to the object, copy data to the buffer
for (key in data){
buffer[key] = data[key];
}
Send buffer to the plugin. And clear data, if desired.
By doing so, you will not iterate more, than the actual data you processed.
I have million of objects each have an unique ID - number.
Each object for making it simple contains name
They objects are being added into array.
Into this array i'm adding and removing objects.
In order to remove object I have the id, and then need to find the index in the array and splice it out.
In my case i have allot of objects and can have allot of removes operations. so in case i have 1000 removes. and all of this objects ids are stored in the end of the array, than i will run over the all 1 million objects till i find them.
Storing the index in the object after adding is not good, because every each remove i need to update the indices of all objects stored after the removed one.
For example removing the first 1000 will cause updating the rest of the 1M-1000 items indices.
My question is, what is the best solution for my problem?
-- UPDATE --
for example: My flat array look like this after adding 1M objects
[ obj1, obj2, obj3, .... obj1000000 ]
I want to remove now the object obj1000000. For finding which index this object
was inserted to i need to run over all the array (or till i found the item) and compare the current item id with my obj1000000 id, and break out from the loop when found. Then remove the item by it's index.
If i would store the index of each object in the object itself after it being added to the array, i would have to update the rest of the objects indices after removing one.
For example: obj1 will contains property index=0, obj2 will have index=1 and so on. To remove obj5 i just get its property index which is 4 and remove it. but now obj6 which has index=5 is incorrect. and should be 4. and obj7 should be 5 and so on. so update must be done.
My SmartArray holds an TypedArray created in some size. And i'm expending it if needed. When push is called. I'm simply set the value in the last item this._array[this.length++] = value; (Checking of course if to expand the array)
SmartArray.prototype.getArray = function () {
return this._array.subarray(0, this.length);
}
SmartArray.prototype.splice = function (index, removeCount) {
if (index + removeCount < this.length) {
var sub = this._array.subarray(index + removeCount, this.length);
this._array.set(sub, index);
} else {
removeCount = this.length - index;
}
this.length -= removeCount;
}
It is working very fast, subarray doesn't create a new array. And set is working very fast as well.
The standard solutions for this problem are
balanced (binary) trees,
hash tables.
They take respectively O(Log(N)) and O(1) operations per search/insertion/deletion on average.
Both can be implemented in an array. You will find numerous versions of them by web search.
I have an ajax call that returns a JSON object that is pretty complex and I'm having a hard time sorting it.
My call:
$.post('/reports-ajax',arguments, function(data) {}
The response:
{
"10001":{
"unitname":"Fort Worth",
"discounts":{"12-02-2012":"34.810000","12-03-2012":"20.810000","12-04-2012":"27.040000"},
"gross":{"12-02-2012":"56.730000","12-03-2012":"19.350000","12-04-2012":"66.390000"},
"net":{"12-02-2012":"61.920000","12-03-2012":"98.540000","12-04-2012":"39.350000"},
"discounts_total":82.66,
"gross_total":82.47,
"net_total":99.81,
"number":10001
},
"10002":{
"unitname":"Dallast",
"discounts":{"12-02-2012":"12.600000","12-03-2012":"25.780000","12-04-2012":"47.780000","12-05-2012":"45.210000"},
"gross":{"12-02-2012":"29.370000","12-03-2012":"91.110000","12-04-2012":"60.890000","12-05-2012":"51.870000"},
"net":{"12-02-2012":"16.770000","12-03-2012":"65.330000","12-04-2012":"13.110000","12-05-2012":"06.660000"},
"discounts_total":131.37,
"gross_total":33.24,
"net_total":101.87,
"number":10002
},
"32402":{
"unitname":"Austin",
"discounts":{"12-05-2012":"52.890000","12-02-2012":"22.430000","12-03-2012":"58.420000","12-04-2012":"53.130000"},
"gross":{"12-05-2012":"25.020000","12-02-2012":"2836.010000","12-03-2012":"54.740000","12-04-2012":"45.330000"},
"net":{"12-04-2012":"92.200000","12-05-2012":"72.130000","12-02-2012":"13.580000","12-03-2012":"96.320000"},
"discounts_total":186.87,
"gross_total":161.1,
"net_total":174.23,
"number":32402
}
}
I go over the function with a standard each call and do some awesome stuff with highcharts but now I'm trying to sort the responses by the net_total call and I can't figure it out.
I tried .sort() and it errors out that its not a function. I've been reading for a while but guess I'm not finding the right results. This looked promising: Sorting an array of JavaScript objects but it failed with the .sort is not a function. It seems most .sort are on [] arrays not full objects..
Any help would be greatly appreciated.
Sorting objects doesn't make sense since object keys have no positional value. For example, this:
{ a:1, b:2 }
and this:
{ b:2, a:1 }
are exactly the same object. They're not just similar, they're the same.
Nothing in javascript per se gives object keys any positional value. Some people perhaps are mistaken in the belief that:
for (var key in obj) {
iterates through the object keys in a specific sequence. But this is wrong. You should always assume that the for .. in loop processes object keys in random order, always, all the time.
Obviously, if you're going to write a web browser, you're not going to implement a random number generator to parse a for .. in loop. Therefore most web browsers have an accidental stability to how the for .. in loop processes object keys.
Developers who learn javascript by playing around with the browser may figure out that their browser iterates through objects in alphabetical order for example, or the order the keys were added to the object. But this is totally accidental and cannot be relied upon. The browser vendor may change this behavior in the future without violating any backwards compatability (except with buggy scripts written by people who believe objects have a sort order). Not to mention that different browsers have different implementations of javascript and therefore not necessarily have the same internal key ordering of objects.
All the above is besides the point. "Key sort order" does not make any sense in javascript and any behavior observed is merely implementation detail. In short, javascript object does not have key order, just assume it's random.
Solution
Now, what you're really trying to do is not sort the object (you can't, it doesn't make sense). What you're really trying to do is process the object attributes in a specific order. The solution is to simply create an array (which has sorting order) of object keys and then process the object using that array:
// First create the array of keys/net_total so that we can sort it:
var sort_array = [];
for (var key in Response) {
sort_array.push({key:key,net_total:Response[key].net_total});
}
// Now sort it:
sort_array.sort(function(x,y){return x.net_total - y.net_total});
// Now process that object with it:
for (var i=0;i<sort_array.length;i++) {
var item = Response[sort_array[i].key];
// now do stuff with each item
}
What you have there isn't an array and has no order, so you'll have to transform it into an array so you can give it order.
Vaguely:
var array = [];
$.each(data, function(key, value) {
array.push(value);
});
array.sort(function(a, b) {
return a.net_total - b.net_total;
});
Live Example | Source
As GolezTroi points out in the comments, normally the above would lose the key that each entry is stored under in data and so you'd add it back in the first $.each loop above, but in this case the entries already have the key on them (as number), so there's no need.
Or you can replace the first $.each with $.map:
var array = $.map(data, function(entry) {
return entry;
});
// ...and then sort etc.
...whichever you prefer.