Javascript - faster, more efficient method to sort a Javascript object based on a sorted _id array?
We have MongoDB documents that looks like this:
var JavascriptObject = {
DbDocs : [
{
_id : "1",
{..more values..}
},
{
_id : "2",
{..more values..}
},
{
_id : "3",
{..more values..}
}
]
}
Based on the defined values ββin JavascriptObject
, we order an array of _id from documents, and the result is:
var OrderedArray = [ 2, 1, 3 ];
We are currently restoring the whole JavascriptObject
by matching the _id in OrderedArray
with the _id in DbDocs
:
var JavascriptObjectToRebuild = [];
var DbDocuments = JavascriptObject.DbDocs;
var DocumentCount = 0;
for (var OrderedNumber in OrderedArray) {
for (var Document in DbDocuments) {
if ( DbDocuments[Document]._id === OrderedArray[OrderedNumber] ) {
JavascriptObjectToRebuild[DocumentCount] = {}; // new Document Object
JavascriptObjectToRebuild[DocumentCount]._id = DbDocuments[Document]._id;
JavascriptObjectToRebuild[DocumentCount]...more values = DbDocuments[Document]...more values;
DocumentCount++; // increment
}
}
}
var SortedJavascriptObject = { DbDocs: [] }; // format for client-side templating
for (var Document in JSONToRebuild) {
SortedJavascriptObject.DbDocs.push(JavascriptObjectToRebuild[Document]);
}
Is there a more efficient way to sort JavascriptObject
based on this OrderedArray
?
source to share
See update below if it cannot be sorted directly and you should use instead OrderedArray
.
If you can apply your criteria in a function callback Array#sort
(for example, if you can do it by comparing two entries in an array with each other), you can just sort JSON.DbDocs
directly.
Here's an example that sorts based on a numeric value _id
; naturally you replace this with your object comparison logic.
Also note that I changed the name of the top-level variable ( JSON
not used like this, and it's not JSON anyway):
var Obj = {
DbDocs : [
{
_id : "2",
more: "two"
},
{
_id : "1",
more: "one"
},
{
_id : "3",
more: "three"
}
]
};
Obj.DbDocs.sort(function(a, b) {
return +a._id - +b._id; // Replace with your logic comparing a and b
});
document.querySelector('pre').innerHTML = JSON.stringify(Obj, null, 2);
<pre></pre>
If it is not possible to sort directly, and you have to work with OrderedArray
, then it is still possible with sort
, but less graceful: you use Array#indexOf
to find out where each entry in an array should be:
Obj.DbDocs.sort(function(a, b) {
return OrderedArray.indexOf(+a._id) - OrderedArray.indexOf(+b._id);
});
( +
converts IDs from strings to numbers, as it OrderedArray
contains numbers in your question, but ID values ββare strings.)
Live example:
var Obj = {
DbDocs : [
{
_id : "1",
more: "one"
},
{
_id : "2",
more: "two"
},
{
_id : "3",
more: "three"
}
]
};
var OrderedArray = [2, 1, 3];
Obj.DbDocs.sort(function(a, b) {
return OrderedArray.indexOf(+a._id) - OrderedArray.indexOf(+b._id);
});
document.querySelector('pre').innerHTML = JSON.stringify(Obj, null, 2);
<pre></pre>
If there OrderedArray
will be many entries in, you can first create a lookup object to avoid a lot of calls indexOf
(which are costly: ( georg did this in response, but he has since deleted it for some reason)
var OrderMap = {}
OrderedArray.forEach(function(entry, index) {
OrderMap[entry] = index;
});
Obj.DbDocs.sort(function(a, b) {
return OrderMap[a._id] - OrderMap[b._id];
});
(We don't need to convert IDs to numbers because property names are always strings, so we convert numbers to strings when we build the map.)
Live example:
var Obj = {
DbDocs : [
{
_id : "1",
more: "one"
},
{
_id : "2",
more: "two"
},
{
_id : "3",
more: "three"
}
]
};
var OrderedArray = [2, 1, 3];
var OrderMap = {}
OrderedArray.forEach(function(entry, index) {
OrderMap[entry] = index;
});
Obj.DbDocs.sort(function(a, b) {
return OrderMap[a._id] - OrderMap[b._id];
});
document.querySelector('pre').innerHTML = JSON.stringify(Obj, null, 2);
<pre></pre>
source to share
As I understand it, you want the result like this,
[{"_id":"2"}, {"_id":"1"}, {"_id":"3"}]
so you can do it with forEach
and indexOf
for example
var JSONDADA = {
DbDocs : [{_id : "1",}, {_id : "2"}, {_id : "3"}]
};
var DbDocuments = JSONDADA.DbDocs;
var OrderedArray = [ 2, 1, 3 ];
var result = [];
DbDocuments.forEach(function (el) {
var position = OrderedArray.indexOf(+el._id);
if (position >= 0) {
result[position] = el;
}
});
console.log(JSON.stringify(result));
source to share