I have an array of numbers that I need to make sure are unique. I found the code snippet below on the internet and it works great until the array has a zero in it. I found this other script here on Stack Overflow that looks almost exactly like it, but it doesn't fail.
So for the sake of helping me learn, can someone help me determine where the prototype script is going wrong?
Array.prototype.getUnique = function() {
var o = {}, a = [], i, e;
for (i = 0; e = this[i]; i++) {o[e] = 1};
for (e in o) {a.push (e)};
return a;
}
More answers from duplicate question:
Remove duplicate values from JS array
Similar question:
Get all non-unique values (i.e.: duplicate/more than one occurrence) in an array
With JavaScript 1.6 / ECMAScript 5 you can use the native filter method of an Array in the following way to get an array with unique values:
function onlyUnique(value, index, array) {
return self.indexOf(value) === index;
}
// usage example:
var a = ['a', 1, 'a', 2, '1'];
var unique = a.filter(onlyUnique);
console.log(unique); // ['a', 1, 2, '1']
The native method filter will loop through the array and leave only those entries that pass the given callback function onlyUnique.
onlyUnique checks, if the given value is the first occurring. If not, it must be a duplicate and will not be copied.
This solution works without any extra library like jQuery or prototype.js.
It works for arrays with mixed value types too.
For old Browsers (<ie9), that do not support the native methods filter and indexOf you can find work arounds in the MDN documentation for filter and indexOf.
If you want to keep the last occurrence of a value, simply replace indexOf with lastIndexOf.
With ES6 this can be shorten to:
// usage example:
var myArray = ['a', 1, 'a', 2, '1'];
var unique = myArray.filter((value, index, array) => array.indexOf(value) === index);
console.log(unique); // unique is ['a', 1, 2, '1']
Thanks to Camilo Martin for hint in comment.
ES6 has a native object Set to store unique values. To get an array with unique values you could now do this:
var myArray = ['a', 1, 'a', 2, '1'];
let unique = [...new Set(myArray)];
console.log(unique); // unique is ['a', 1, 2, '1']
The constructor of Set takes an iterable object, like an Array, and the spread operator ... transform the set back into an Array. Thanks to Lukas Liese for hint in comment.
Updated answer for ES6/ES2015: Using the Set and the spread operator (thanks le-m), the single line solution is:
let uniqueItems = [...new Set(items)]
Which returns
[4, 5, 6, 3, 2, 23, 1]
I split all answers to 4 possible solutions:
Use object { } to prevent duplicates
Use helper array [ ]
Use filter + indexOf
Bonus! ES6 Sets method.
Here's sample codes found in answers:
Use object { } to prevent duplicates
function uniqueArray1( ar ) {
var j = {};
ar.forEach( function(v) {
j[v+ '::' + typeof v] = v;
});
return Object.keys(j).map(function(v){
return j[v];
});
}
Use helper array [ ]
function uniqueArray2(arr) {
var a = [];
for (var i=0, l=arr.length; i<l; i++)
if (a.indexOf(arr[i]) === -1 && arr[i] !== '')
a.push(arr[i]);
return a;
}
Use filter + indexOf
function uniqueArray3(a) {
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
// usage
var unique = a.filter( onlyUnique ); // returns ['a', 1, 2, '1']
return unique;
}
Use ES6 [...new Set(a)]
function uniqueArray4(a) {
return [...new Set(a)];
}
And I wondered which one is faster. I've made sample Google Sheet to test functions. Note: ECMA 6 is not avaliable in Google Sheets, so I can't test it.
Here's the result of tests:
I expected to see that code using object { } will win because it uses hash. So I'm glad that tests showed the best results for this algorithm in Chrome and IE. Thanks to #rab for the code.
Update 2020
Google Script enabled ES6 Engine. Now I tested the last code with Sets and it appeared faster than the object method.
You can also use underscore.js.
console.log(_.uniq([1, 2, 1, 3, 1, 4]));
<script src="http://underscorejs.org/underscore-min.js"></script>
which will return:
[1, 2, 3, 4]
One Liner, Pure JavaScript
With ES6 syntax
list = list.filter((x, i, a) => a.indexOf(x) == i)
x --> item in array
i --> index of item
a --> array reference, (in this case "list")
With ES5 syntax
list = list.filter(function (x, i, a) {
return a.indexOf(x) == i;
});
Browser Compatibility: IE9+
Remove duplicates using Set.
Array with duplicates
const withDuplicates = [2, 2, 5, 5, 1, 1, 2, 2, 3, 3];
Get a new array without duplicates by using Set
const withoutDuplicates = Array.from(new Set(withDuplicates));
A shorter version
const withoutDuplicates = [...new Set(withDuplicates)];
Result: [2, 5, 1, 3]
Many of the answers here may not be useful to beginners. If de-duping an array is difficult, will they really know about the prototype chain, or even jQuery?
In modern browsers, a clean and simple solution is to store data in a Set, which is designed to be a list of unique values.
const cars = ['Volvo', 'Jeep', 'Volvo', 'Lincoln', 'Lincoln', 'Ford'];
const uniqueCars = Array.from(new Set(cars));
console.log(uniqueCars);
The Array.from is useful to convert the Set back to an Array so that you have easy access to all of the awesome methods (features) that arrays have. There are also other ways of doing the same thing. But you may not need Array.from at all, as Sets have plenty of useful features like forEach.
If you need to support old Internet Explorer, and thus cannot use Set, then a simple technique is to copy items over to a new array while checking beforehand if they are already in the new array.
// Create a list of cars, with duplicates.
var cars = ['Volvo', 'Jeep', 'Volvo', 'Lincoln', 'Lincoln', 'Ford'];
// Create a list of unique cars, to put a car in if we haven't already.
var uniqueCars = [];
// Go through each car, one at a time.
cars.forEach(function (car) {
// The code within the following block runs only if the
// current car does NOT exist in the uniqueCars list
// - a.k.a. prevent duplicates
if (uniqueCars.indexOf(car) === -1) {
// Since we now know we haven't seen this car before,
// copy it to the end of the uniqueCars list.
uniqueCars.push(car);
}
});
To make this instantly reusable, let's put it in a function.
function deduplicate(data) {
if (data.length > 0) {
var result = [];
data.forEach(function (elem) {
if (result.indexOf(elem) === -1) {
result.push(elem);
}
});
return result;
}
}
So to get rid of the duplicates, we would now do this.
var uniqueCars = deduplicate(cars);
The deduplicate(cars) part becomes the thing we named result when the function completes.
Just pass it the name of any array you like.
Using ES6 new Set
var array = [3,7,5,3,2,5,2,7];
var unique_array = [...new Set(array)];
console.log(unique_array); // output = [3,7,5,2]
Using For Loop
var array = [3,7,5,3,2,5,2,7];
for(var i=0;i<array.length;i++) {
for(var j=i+1;j<array.length;j++) {
if(array[i]===array[j]) {
array.splice(j,1);
}
}
}
console.log(array); // output = [3,7,5,2]
I have since found a nice method that uses jQuery
arr = $.grep(arr, function(v, k){
return $.inArray(v ,arr) === k;
});
Note: This code was pulled from Paul Irish's duck punching post - I forgot to give credit :P
Magic
a.filter(e=>!(t[e]=e in t))
O(n) performance - we assume your array is in a and t={}. Explanation here (+Jeppe impr.)
let unique = (a,t={}) => a.filter(e=>!(t[e]=e in t));
// "stand-alone" version working with global t:
// a1.filter((t={},e=>!(t[e]=e in t)));
// Test data
let a1 = [5,6,0,4,9,2,3,5,0,3,4,1,5,4,9];
let a2 = [[2, 17], [2, 17], [2, 17], [1, 12], [5, 9], [1, 12], [6, 2], [1, 12]];
let a3 = ['Mike', 'Adam','Matt', 'Nancy', 'Adam', 'Jenny', 'Nancy', 'Carl'];
// Results
console.log(JSON.stringify( unique(a1) ))
console.log(JSON.stringify( unique(a2) ))
console.log(JSON.stringify( unique(a3) ))
The simplest, and fastest (in Chrome) way of doing this:
Array.prototype.unique = function() {
var a = [];
for (var i=0, l=this.length; i<l; i++)
if (a.indexOf(this[i]) === -1)
a.push(this[i]);
return a;
}
Simply goes through every item in the array, tests if that item is already in the list, and if it's not, pushes to the array that gets returned.
According to JSBench, this function is the fastest of the ones I could find anywhere - feel free to add your own though.
The non-prototype version:
function uniques(arr) {
var a = [];
for (var i=0, l=arr.length; i<l; i++)
if (a.indexOf(arr[i]) === -1 && arr[i] !== '')
a.push(arr[i]);
return a;
}
Sorting
When also needing to sort the array, the following is the fastest:
Array.prototype.sortUnique = function() {
this.sort();
var last_i;
for (var i=0;i<this.length;i++)
if ((last_i = this.lastIndexOf(this[i])) !== i)
this.splice(i+1, last_i-i);
return this;
}
or non-prototype:
function sortUnique(arr) {
arr.sort();
var last_i;
for (var i=0;i<arr.length;i++)
if ((last_i = arr.lastIndexOf(arr[i])) !== i)
arr.splice(i+1, last_i-i);
return arr;
}
This is also faster than the above method in most non-Chrome browsers.
We can do this using ES6 sets:
var duplicatesArray = [1, 2, 3, 4, 5, 1, 1, 1, 2, 3, 4];
var uniqueArray = [...new Set(duplicatesArray)];
console.log(uniqueArray); // [1,2,3,4,5]
["Defects", "Total", "Days", "City", "Defects"].reduce(function(prev, cur) {
return (prev.indexOf(cur) < 0) ? prev.concat([cur]) : prev;
}, []);
[0,1,2,0,3,2,1,5].reduce(function(prev, cur) {
return (prev.indexOf(cur) < 0) ? prev.concat([cur]) : prev;
}, []);
After looking into all the 90+ answers here, I saw there is room for one more:
Array.includes has a very handy second-parameter: "fromIndex", so by using it, every iteration of the filter callback method will search the array, starting from [current index] + 1 which guarantees not to include currently filtered item in the lookup and also saves time.
Note - this solution does not retain the order, as it removed duplicated items from left to right, but it wins the Set trick if the Array is a collection of Objects.
// π© π© π©
var list = [0,1,2,2,3,'a','b',4,5,2,'a']
console.log(
list.filter((v,i) => !list.includes(v,i+1))
)
// [0,1,3,"b",4,5,2,"a"]
Explanation:
For example, lets assume the filter function is currently iterating at index 2) and the value at that index happens to be 2. The section of the array that is then scanned for duplicates (includes method) is everything after index 2 (i+1):
π π
[0, 1, 2, 2 ,3 ,'a', 'b', 4, 5, 2, 'a']
π |---------------------------|
And since the currently filtered item's value 2 is included in the rest of the array, it will be filtered out, because of the leading exclamation mark which negates the filter rule.
If order is important, use this method:
// π© π© π©
var list = [0,1,2,2,3,'a','b',4,5,2,'a']
console.log(
// Initialize with empty array and fill with non-duplicates
list.reduce((acc, v) => (!acc.includes(v) && acc.push(v), acc), [])
)
// [0,1,2,3,"a","b",4,5]
This has been answered a lot, but it didn't address my particular need.
Many answers are like this:
a.filter((item, pos, self) => self.indexOf(item) === pos);
But this doesn't work for arrays of complex objects.
Say we have an array like this:
const a = [
{ age: 4, name: 'fluffy' },
{ age: 5, name: 'spot' },
{ age: 2, name: 'fluffy' },
{ age: 3, name: 'toby' },
];
If we want the objects with unique names, we should use array.prototype.findIndex instead of array.prototype.indexOf:
a.filter((item, pos, self) => self.findIndex(v => v.name === item.name) === pos);
This prototype getUnique is not totally correct, because if i have a Array like: ["1",1,2,3,4,1,"foo"] it will return ["1","2","3","4"] and "1" is string and 1 is a integer; they are different.
Here is a correct solution:
Array.prototype.unique = function(a){
return function(){ return this.filter(a) }
}(function(a,b,c){ return c.indexOf(a,b+1) < 0 });
using:
var foo;
foo = ["1",1,2,3,4,1,"foo"];
foo.unique();
The above will produce ["1",2,3,4,1,"foo"].
You can simlply use the built-in functions Array.prototype.filter() and Array.prototype.indexOf()
array.filter((x, y) => array.indexOf(x) == y)
var arr = [1, 2, 3, 3, 4, 5, 5, 5, 6, 7, 8, 9, 6, 9];
var newarr = arr.filter((x, y) => arr.indexOf(x) == y);
console.log(newarr);
[...new Set(duplicates)]
This is the simplest one and referenced from MDN Web Docs.
const numbers = [2,3,4,4,2,3,3,4,4,5,5,6,6,7,5,32,3,4,5]
console.log([...new Set(numbers)]) // [2, 3, 4, 5, 6, 7, 32]
Array.prototype.getUnique = function() {
var o = {}, a = []
for (var i = 0; i < this.length; i++) o[this[i]] = 1
for (var e in o) a.push(e)
return a
}
Without extending Array.prototype (it is said to be a bad practice) or using jquery/underscore, you can simply filter the array.
By keeping last occurrence:
function arrayLastUnique(array) {
return array.filter(function (a, b, c) {
// keeps last occurrence
return c.indexOf(a, b + 1) < 0;
});
},
or first occurrence:
function arrayFirstUnique(array) {
return array.filter(function (a, b, c) {
// keeps first occurrence
return c.indexOf(a) === b;
});
},
Well, it's only javascript ECMAScript 5+, which means only IE9+, but it's nice for a development in native HTML/JS (Windows Store App, Firefox OS, Sencha, Phonegap, Titanium, ...).
That's because 0 is a falsy value in JavaScript.
this[i] will be falsy if the value of the array is 0 or any other falsy value.
Now using sets you can remove duplicates and convert them back to the array.
var names = ["Mike","Matt","Nancy", "Matt","Adam","Jenny","Nancy","Carl"];
console.log([...new Set(names)])
Another solution is to use sort & filter
var names = ["Mike","Matt","Nancy", "Matt","Adam","Jenny","Nancy","Carl"];
var namesSorted = names.sort();
const result = namesSorted.filter((e, i) => namesSorted[i] != namesSorted[i+1]);
console.log(result);
If you're using Prototype framework there is no need to do 'for' loops, you can use http://prototypejs.org/doc/latest/language/Array/prototype/uniq/ like this:
var a = Array.uniq();
Which will produce a duplicate array with no duplicates. I came across your question searching a method to count distinct array records so after uniq() I used size() and there was my simple result.
p.s. Sorry if i mistyped something
edit: if you want to escape undefined records you may want to add compact() before, like this:
var a = Array.compact().uniq();
I had a slightly different problem where I needed to remove objects with duplicate id properties from an array. this worked.
let objArr = [{
id: '123'
}, {
id: '123'
}, {
id: '456'
}];
objArr = objArr.reduce((acc, cur) => [
...acc.filter((obj) => obj.id !== cur.id), cur
], []);
console.log(objArr);
If you're okay with extra dependencies, or you already have one of the libraries in your codebase, you can remove duplicates from an array in place using LoDash (or Underscore).
Usage
If you don't have it in your codebase already, install it using npm:
npm install lodash
Then use it as follows:
import _ from 'lodash';
let idArray = _.uniq ([
1,
2,
3,
3,
3
]);
console.dir(idArray);
Out:
[ 1, 2, 3 ]
I'm not sure why Gabriel Silveira wrote the function that way but a simpler form that works for me just as well and without the minification is:
Array.prototype.unique = function() {
return this.filter(function(value, index, array) {
return array.indexOf(value, index + 1) < 0;
});
};
or in CoffeeScript:
Array.prototype.unique = ->
this.filter( (value, index, array) ->
array.indexOf(value, index + 1) < 0
)
Finding unique Array values in simple method
function arrUnique(a){
var t = [];
for(var x = 0; x < a.length; x++){
if(t.indexOf(a[x]) == -1)t.push(a[x]);
}
return t;
}
arrUnique([1,4,2,7,1,5,9,2,4,7,2]) // [1, 4, 2, 7, 5, 9]
It appears we have lost Rafael's answer, which stood as the accepted answer for a few years. This was (at least in 2017) the best-performing solution if you don't have a mixed-type array:
Array.prototype.getUnique = function(){
var u = {}, a = [];
for (var i = 0, l = this.length; i < l; ++i) {
if (u.hasOwnProperty(this[i])) {
continue;
}
a.push(this[i]);
u[this[i]] = 1;
}
return a;
}
If you do have a mixed-type array, you can serialize the hash key:
Array.prototype.getUnique = function() {
var hash = {}, result = [], key;
for ( var i = 0, l = this.length; i < l; ++i ) {
key = JSON.stringify(this[i]);
if ( !hash.hasOwnProperty(key) ) {
hash[key] = true;
result.push(this[i]);
}
}
return result;
}
strange this hasn't been suggested before.. to remove duplicates by object key (id below) in an array you can do something like this:
const uniqArray = array.filter((obj, idx, arr) => (
arr.findIndex((o) => o.id === obj.id) === idx
))
For an object-based array with some unique id's, I have a simple solution through which you can sort in linear complexity
function getUniqueArr(arr){
const mapObj = {};
arr.forEach(a => {
mapObj[a.id] = a
})
return Object.values(mapObj);
}
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!
target = {}
target[' ' + key] = value // numeric key
This can prevent automatic sort of Object numeric property.
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
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.
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
To prevent the automatic sort of numeric keys of Object in Javascript, the best way is to tweak the Object keys a little bit.
We can insert an "e" in front of every key name to avoid lexicographical sorting of keys and to get the proper output slice the "e", by using the following code;
object_1 = {
"3": 11,
"2": 12,
"1": 13
}
let automaticSortedKeys = Object.keys(object_1);
console.log(automaticSortedKeys) //["1", "2", "3"]
object_2 = {
"e3": 11,
"e2": 12,
"e1": 13
}
let rawObjectKeys = Object.keys(object_2);
console.log(rawObjectKeys) //["e3", "e2", "e1"]
let properKeys = rawObjectKeys.map(function(element){
return element.slice(1)
});
console.log(properKeys) //["3", "2", "1"]
instead of generating an object like {5: 2, 2: 2, 1: 1}
generate an array to the effect of
[
{key: 5, val: 2},
{key: 2, val: 2},
{key: 1, val: 1}
]
or... keep track of the sort order in a separate value or key
I've stumbled with this issue with our normalised array which keyed with Ids> After did my research, I found out there's no way to fix using the object keys because by default the Javascript is sorting any object key with number when you iterate it.
The solution I've done and it worked for me is to put a 'sortIndex' field and used that to sort the list.
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 :)
I came across this same problem, and after search a lot about that, i found out that the solution to prevent this behavior is make key as string.
Like that:
{"a": 2, "b": 2}
you can use Map() in javascript ES6 which will keep the order of the keys insertion.
just trying to solve your problem in an alternative solution, recently like to practise leetcode-like question
function solution(arr) {
const obj = {};
const record = {
value: null,
count: 0
};
for (let i = 0; i < arr.length; i++) {
let current = arr[i];
if (!obj[current]) {
obj[current] = 0;
}
obj[current]++;
if (obj[current] > record.count) {
record.value = current;
record.count = obj[current];
}
}
console.log("mode number: ", record.value);
console.log("mode number count: ", record.count);
}
simply do that while you're working with a numeric array index
data = {}
data[key] = value
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!
target = {}
target[' ' + key] = value // numeric key
This can prevent automatic sort of Object numeric property.
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
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.
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
To prevent the automatic sort of numeric keys of Object in Javascript, the best way is to tweak the Object keys a little bit.
We can insert an "e" in front of every key name to avoid lexicographical sorting of keys and to get the proper output slice the "e", by using the following code;
object_1 = {
"3": 11,
"2": 12,
"1": 13
}
let automaticSortedKeys = Object.keys(object_1);
console.log(automaticSortedKeys) //["1", "2", "3"]
object_2 = {
"e3": 11,
"e2": 12,
"e1": 13
}
let rawObjectKeys = Object.keys(object_2);
console.log(rawObjectKeys) //["e3", "e2", "e1"]
let properKeys = rawObjectKeys.map(function(element){
return element.slice(1)
});
console.log(properKeys) //["3", "2", "1"]
instead of generating an object like {5: 2, 2: 2, 1: 1}
generate an array to the effect of
[
{key: 5, val: 2},
{key: 2, val: 2},
{key: 1, val: 1}
]
or... keep track of the sort order in a separate value or key
I've stumbled with this issue with our normalised array which keyed with Ids> After did my research, I found out there's no way to fix using the object keys because by default the Javascript is sorting any object key with number when you iterate it.
The solution I've done and it worked for me is to put a 'sortIndex' field and used that to sort the list.
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 :)
I came across this same problem, and after search a lot about that, i found out that the solution to prevent this behavior is make key as string.
Like that:
{"a": 2, "b": 2}
you can use Map() in javascript ES6 which will keep the order of the keys insertion.
just trying to solve your problem in an alternative solution, recently like to practise leetcode-like question
function solution(arr) {
const obj = {};
const record = {
value: null,
count: 0
};
for (let i = 0; i < arr.length; i++) {
let current = arr[i];
if (!obj[current]) {
obj[current] = 0;
}
obj[current]++;
if (obj[current] > record.count) {
record.value = current;
record.count = obj[current];
}
}
console.log("mode number: ", record.value);
console.log("mode number count: ", record.count);
}
simply do that while you're working with a numeric array index
data = {}
data[key] = value