How to prevent automatic sort of Object numeric property?

28,409

Solution 1

You are using a JS object, that by definition does not keep order. Think of it as a key => value map.

You should be using an array, that will keep whatever you insert on the index you inserted it into. Think of it as a list.

Also notice that you did not in fact "reverse the order of the assignment", because you inserted elements on the same index every time.

Solution 2

target = {}
target[' ' + key] = value // numeric key

This can prevent automatic sort of Object numeric property.

Solution 3

You really can't rely on order of an object fields in JavaScript, but I can suggest to use Map (ES6/ES2015 standard) if you need to preserve order of your key, value pair object. See the snippet below:

let myObject = new Map();
myObject.set('z', 33);
myObject.set('1', 100);
myObject.set('b', 3);

for (let [key, value] of myObject) {
  console.log(key, value);
}
// z 33
// 1 100
// b 3

Solution 4

This is an old topic but it is still worth mentioning as it is hard to find a straight explanation in one-minute googling.

I recently had a coding exercise that finding the first occurrence of the least/most frequent integer in an array, it is pretty much the same as your case.

I encountered the same problem as you, having the numeric keys sorted by ASC in JavaScript object, which is not preserving the original order of elements, which is the default behavior in js.

A better way to solve this in ES6 is to use a new data type called: Map

Map can preserve the original order of elements(pairs), and also have the unique key benefit from object.

let map = new Map()
map.set(4, "first") // Map(1) {4 => "first"}
map.set(1, "second") // Map(2) {4 => "first", 1 => "second"}
map.set(2, "third") // Map(3) {4 => "first", 1 => "second", 2 => "third"}
for(let [key, value] of map) {
  console.log(key, value)
}
// 4 "first"
// 1 "second"
// 2 "third"

However, using the object data type can also solve the problem, but we need the help of the input array to get back the original order of elements:

function findMostAndLeast(arr) {
  let countsMap = {};
  let mostFreq = 0;
  let leastFreq = arr.length;
  let mostFreqEl, leastFreqEl;

  for (let i = 0; i < arr.length; i++) {
    let el = arr[i];
    // Count each occurrence
    if (countsMap[el] === undefined) {
      countsMap[el] = 1;
    } else {
      countsMap[el] += 1;
    }
  }

  // Since the object is sorted by keys by default in JS, have to loop again the original array
  for (let i = 0; i < arr.length; i++) {
    const el = arr[i];

    // find the least frequent 
    if (leastFreq > countsMap[el]) {
      leastFreqEl = Number(el);
      leastFreq = countsMap[el];
    }

    // find the most frequent 
    if (countsMap[el] > mostFreq) {
      mostFreqEl = Number(el);
      mostFreq = countsMap[el];
    }
  }

  return {
    most_frequent: mostFreqEl,
    least_frequent: leastFreqEl
  }
}
const testData = [6, 1, 3, 2, 4, 7, 8, 9, 10, 4, 4, 4, 10, 1, 1, 1, 1, 6, 6, 6, 6];    
console.log(findMostAndLeast(testData)); // { most_frequent: 6, least_frequent: 3 }, it gets 6, 3 instead of 1, 2 

Solution 5

The simplest and the best way to preserve the order of the keys in the array obtained by Object.keys() is to manipulate the Object keys a little bit.

insert a "_" in front of every key name. then run the following code!

myObject = {
  _a: 1,
  _1: 2,
  _2: 3
}

const myObjectRawKeysArray = Object.keys(myObject); 
console.log(myObjectRawKeysArray)
//["_a", "_1", "_2"]

const myDesiredKeysArray = myObjectRawKeysArray.map(rawKey => {return rawKey.slice(1)}); 
console.log(myDesiredKeysArray)
//["a", "1", "2"]

You get the desired order in the array with just a few lines of code. hApPy CoDiNg :)

Share:
28,409
AndyHu
Author by

AndyHu

Front-end developer. Passionate about web development! React, Redux, Bootstrap, .NET, Sql Server, MongoDB

Updated on July 09, 2022

Comments

  • AndyHu
    AndyHu almost 2 years

    Why I met this problem: I tried to solve an algorithm problem and I need to return the number which appeared most of the times in an array. Like [5,4,3,2,1,1] should return 1. And also when two number appear same time as the maximum appearance return the one came first. Like [5,5,2,2,1] return 5 because 5 appear first. I use an object to store the appearance of each number. The key is the number itself.

    So When the input is [5,5,2,2,1] my object should be Object {5: 2, 2: 2, 1: 1} but actually I got Object {1: 1, 2: 2, 5: 2} So When I use for..in to iterate the object I got 2 returned instead of 5 . So that's why I asked this question.

    This problem occurs in Chrome console and I'm not sure if this is a common issue: When I run the following code

    var a = {};
    a[0]=1;
    a[1]=2;
    a[2]=3;
    

    a is: Object {0: 1, 1: 2, 2: 3}

    But when I reverse the order of assignment like:

     var a = {};
     a[2]=3;
     a[1]=2;
     a[0]=1;
    

    a is also:Object {0: 1, 1: 2, 2: 3} The numeric property automatic sorted in ascending order. I tried prefix or postfix the numeric property like

    var a = {};
    a['p'+0]=1;
    a['p'+1]=2;
    a['p'+2]=3;
    console.log(a);//Object {p0: 1, p1: 2, p2: 3}
    

    And this keep the property order. Is this the best way to solve the problem? And is there anyway to prevent this auto sort behavior? Is this only happen in Chrome V8 JavaScript engine? Thank you in advance!

  • AndyHu
    AndyHu over 8 years
    Yeah I know this. Seems like I didn't explain my problem clearly...I've edit my question.
  • Marcelo
    Marcelo over 8 years
    @AndyHu your problem remains the same: you are trying to keep order in an object that simply does not keep order. You probably need a better data structure to solve the other problem you have, using arrays for the ordering and maybe an object for the count.
  • AndyHu
    AndyHu over 8 years
    Ok I'll try another way. Thank you!
  • Ale
    Ale almost 6 years
    This is the same as @AndyHu has suggested to use a prefix. The difference is that in your case it is a prefix with a space instead a letter.
  • AndyHu
    AndyHu almost 6 years
    Yes, the key should be string except for the string value of a number. If you do something like let obj = {}, obj["5"] = 2; obj["2"] = 2, the key order will still be 2,5 which is not you want. Have to use some prefix.
  • Ivan Kleshnin
    Ivan Kleshnin over 2 years
    The answer is outdated. JS objects have ordered keys since ES2015 (yes, by spec). It's just that numeric keys behave differently.