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

?

+3


source to share


2 answers


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>
      

Run codeHide result



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>
      

Run codeHide result


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>
      

Run codeHide result


+3


source


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));
      

Run codeHide result


+1


source







All Articles