2

I want to find occurrences of number in an array and then display the numbers and the occurrences with the numbers sorted in an ascending order like this:

let arr = [9,-10,2,9,6,1,2,10,-8,-10,2,9,6,1];

// {'-10': 2, '-8': 1, '1': 2, '2': 3, '6': 2, '9': 3, '10': 1}

I've found the occurrences of each number in the array by storing them in an object. When I tried console.log the numCount, all the numbers are sorted in an ascending order except for the negative numbers.

/*Find occurrences*/
let numCount = {};
for(let num of arr){
    numCount[num] = numCount[num] ? numCount[num] + 1 : 1;
}
console.log(numCount);
//{ '1': 2, '2': 3, '6': 2, '9': 3, '10': 1, '-10': 2, '-8': 1 }

I look it up on how to sort object and it seems like the way to do it is by storing them in an array and then sort it. So that's what I tried:

/*Store them in array and then sort it*/
let sortedArray = [];
for(let item in numCount){
    sortedArray.push([item, numCount[item]]);
}
sortedArray.sort(function(a,b){
    return a[0] - b[0];
});

/*
console.log(sortedArray);
[
  [ '-10', 2 ],
  [ '-8', 1 ],
  [ '1', 2 ],
  [ '2', 3 ],
  [ '6', 2 ],
  [ '9', 3 ],
  [ '10', 1 ]
]
*/

This next method supposed to display them in an object sorted in ascending order but when I tried it, it displayed them with the negative numbers in the end of that object just like in the beginning. So this is the part where I'm stuck.

let sortedObject = {};
sortedArray.forEach(function(item){
    sortedObject[item[0]] = item[1];
})
/*console.log(sortedObject);
{ '1': 2, '2': 3, '6': 2, '9': 3, '10': 1, '-10': 2, '-8': 1 }
*/

Full code:

let arr = [9,-10,2,9,6,1,2,10,-8,-10,2,9,6,1];

/*Find occurrences*/
let numCount = {};
for(let num of arr){
    numCount[num] = numCount[num] ? numCount[num] + 1 : 1;
}

/*Store them in array and then sort it*/
let sortedArray = [];
for(let item in numCount){
    sortedArray.push([item, numCount[item]]);
}
sortedArray.sort(function(a,b){
    return a[0] - b[0];
});


let sortedObject = {};
sortedArray.forEach(function(item){
    sortedObject[item[0]] = item[1];
})

console.log(sortedObject);
jabaa
  • 5,844
  • 3
  • 9
  • 30
takmil
  • 25
  • 5
  • 1
    Object properties aren't ordered. This is a fool's errand. – MikeM Nov 02 '21 at 18:35
  • 1
    Why do you need it to be an object, rather than (for example) your sortedArray? – Ben Stephens Nov 02 '21 at 18:37
  • 1
    @MikeM [Does JavaScript guarantee object property order?](https://stackoverflow.com/questions/5525795/does-javascript-guarantee-object-property-order) They are. – jabaa Nov 02 '21 at 18:38
  • 1
    I guess the problem here is the keys (or at least some of them) are considered integer-like and you can't change the order. – jabaa Nov 02 '21 at 18:41

3 Answers3

2

There are rules about how properties/keys are sorted in an object and they are reliably ordered if you know the rules.

If the property is a positive number-like (in fact all properties are string actually even if they look like numbers), It will be sorted acceding. if not, it will sorted by the order of insertion. but how does object determine whether a property value is number-like or not?

Consider key '9' : first it is converted to a number which is 9. since it is positive then it is converted back to string which is '9'. same as the first value. so it is considered as a positive number-like for positive number-like.

Consider key '-10' : first it is converted to a number which is -10.

since it is not positive, it is considered as a string... and same for the rest.

So it is now clear that sorting for positive number-like properties was not really necessary at all. whether the input array is sorted or not, result will be the same and order is determined by rules.

solution:

Consider key '09' : first it is converted to a number which is 9. since it is positive then it is converted back to string which is '9'. not same as the first value. so it is considered as a string.

So let's put a '0' before every positive number-like and convert it to string after the sort.

sortedArray.forEach(function(item){
    let key = parseInt(item[0])>0 ? '0'+item[0] : item[0]
    sortedObject[key] = item[1];
})
//{ "-10": 2, "-8": 1, 01: 2, 02: 3, 06: 2, 09: 3, 010: 1 }

be aware that behavior of parseInt for numbers like 010 is different, since it assumes they are Octal.

Another and better solution is using map, read more about Maps and its differences with Objects here.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
s.sohi
  • 128
  • 5
  • "Conclusion: Even in ES2015 you shouldn't rely on the property order of normal objects in JavaScript. It is prone to errors. " - [T. J. Crowder](https://stackoverflow.com/a/38218582/1565512). – MikeM Nov 02 '21 at 23:22
1

Possibly a Map would allow you to do what you want to do with the object whilst allowing you to set a key order.

let arr = [9,-10,2,9,6,1,2,10,-8,-10,2,9,6,1];

/*Find occurrences*/
let numCount = new Map();
for(let num of arr){
  numCount.set(num, (numCount.get(num) ?? 0) + 1);
}

let sortedMap = new Map([...numCount.entries()].sort((a, b) => a[0] - b[0]));

console.log(sortedMap.get(-10));
console.log(...sortedMap.keys());
console.log(...sortedMap.values());
Ben Stephens
  • 3,303
  • 1
  • 4
  • 8
1

Object properties aren't reliably ordered, and how an object is displayed will depend on the particular implementation of console.log.

If, oddly, it is just a matter of display then you could use, for example

const sortedArray = [[-10, 2], [-8, 1], [1, 2], [2, 3], [6, 2], [9, 3], [10, 1]];

let result = `{${sortedArray.map(el => `'${el[0]}': ${el[1]}`).join(', ')}}`;

console.log(result);
MikeM
  • 13,156
  • 2
  • 34
  • 47