Hello! There is an array

var arr=["aa","aa","ab","ab","ac","a","s",]; 
Please tell me how to calculate the same number of elements and output to the array as array [aa: 3, ab: 2, ac: 1, a: 1, s: 1]. On the Internet, I found a solution in the form

 var arr = ["aa", "aa", "ab", "ab", "ac", "a", "s", ]; var arr2 = []; for (i in arr) { if (arr2[arr[i]] != undefined) { (arr2[arr[i]] ++) } else { (arr2[arr[i]] = 1) } } console.log(arr2); 
But the difficulty is that in arr2, the values ​​are not output to a single element of the array, but to one. Because of this, I cannot use arr.sort () to sort by the number of repetitions. If someone can help, I will be sooo happy!

  • [aa: 3, ab: 2, ac: 1, a: 1, s: 1] - this is not possible in javascript - this is a syntax error - Grundy
  • And then in what form can you imagine? Just my ultimate goal is to sort by the number of repetitions. To the most frequent element was at the top, etc. descending. - Alexander
  • and the number of repetitions needed? - Grundy
  • Yes. It should turn out to be something like aa-2 ab-2 ac-1 a-1 s-1 - Alexander

5 answers 5

The sort function can take a comparison function, in which you can just compare the number of repetitions, and not fix elements.

In addition, since non-numeric keys are added to the array, you can do without the second array:

 for (var len = arr.length, i = len; --i >= 0;) { if (arr[arr[i]]) { arr[arr[i]] += 1; arr.splice(i, 1); } else { arr[arr[i]] = 1; } } 

Since the added properties are not numeric, they are not involved in sorting, but they can be accessed in the comparison function:

 arr.sort(function(a, b) { return arr[b] - arr[a]; }); 

For output, you can use several ways: for example, get a new array with elements in which there will be fields corresponding to both the element and the number of its repetitions. Or use the JSON.stringify function

Example:

 var arr = ["aa", "aa", "ab", "ab", "ac", "a", "s"]; for (var len = arr.length, i = len; --i >= 0;) { if (arr[arr[i]]) { arr[arr[i]] += 1; arr.splice(i, 1); } else { arr[arr[i]] = 1; } } arr.sort(function(a, b) { return arr[b] - arr[a]; }); console.log(arr); var stringResult = JSON.stringify(arr, function(k, v) { if (k == '') return v; return `${v} - ${arr[v]}`; }, 2); document.getElementById('result').innerHTML = stringResult; console.log(stringResult); console.log(arr.map((el, i, a) => ({ [el]: a[el] }))); 
 <pre id="result"></pre> 

In order not to work with an array as with objects, you can use the reduce function and immediately get an array that can be sorted.

 var arr = ["aa", "aa", "ab", "ab", "ac", "a", "s", "s", "s"]; var resultReduce = arr.reduce(function(acc, cur) { if (!acc.hash[cur]) { acc.hash[cur] = { [cur]: 1 }; acc.map.set(acc.hash[cur], 1); acc.result.push(acc.hash[cur]); } else { acc.hash[cur][cur] += 1; acc.map.set(acc.hash[cur], acc.hash[cur][cur]); } return acc; }, { hash: {}, map: new Map(), result: [] }); var result = resultReduce.result.sort(function(a, b) { return resultReduce.map.get(b) - resultReduce.map.get(a); }); console.log(result); 

  • splice juzat as suspicious - he has in the worst case the complexity of o(n) is better in memory a little longer than the brakes will be in time) - ampawd
  • @ampawd, so that there are brakes, the array must be quite large. Plus, it is not known exactly how splice is implemented and optimized in a particular browser, so nothing can be said about its complexity - Grundy
  • then the same can be said about using Map - ampawd
  • but just do something about complexity - ampawd
  • @ampawd, do you think nothing has changed at all for five years? Moreover, an example of a single V8 was considered, and even there, it was shown that a collection with millions of elements was noticeably needed. - Grundy

here are two solutions for you - the second uses the es6 Map structure, the first one is pretty banal.

And for sorting you need to get an array, for example, like this

[{arrayItem: aa, count: 2}, {arrayItem: ab, count: 2}, ...]

in order to be able to sort these elements by the count field, and the indices must be integer, you have them string, and therefore do not sort them.

 var arr = ["aa", "aa", "ab", "ab", "ac", "a", "s", "s", "s"]; function getCountsSorted_1(arr) { var counts = []; var res = []; for (var i in arr) { if (counts[arr[i]]) { (counts[arr[i]]++); } else { (counts[arr[i]] = 1) } } var j = 0; for (var i in counts) { res[j++] = { arrayItem: i, count: counts[i] }; } return res.sort(function(a, b) { return a.count < b.count; }).map(function(entry) { var ret = {}; ret[entry.arrayItem] = entry.count; return ret; }); } function getCountsSorted_2(arr) { var counts = new Map(); for (var i in arr) { if (counts.has(arr[i])) { counts.set(arr[i], counts.get(arr[i]) + 1); } else { counts.set(arr[i], 1); } } return Array.from(counts).sort(function(a, b) { return a[1] < b[1]; }).map(function(entry) { var ret = {}; ret[entry[0]] = entry[1]; return ret; }); } console.log(getCountsSorted_1(arr)); console.log(getCountsSorted_2(arr)); 

  • What is the point of using Map ? - Grundy
  • @Grundy convenience - ampawd
  • in this case, extra calls to has, set, get. Using a normal object, you can do without them - Grundy
  • @Grundy well, you can also use your usual object - you would give at least one of your decisions (if any)) - ampawd
  • @Grundy the point is to use Array.from further, it will not turn an ordinary object into an array - ampawd

 var arr = ["aa", "aa", "ab", "ab", "ab", "ac", "a", "s", ]; var arr2 = {}; var arr3 = {}; for (var i = 0; i < arr.length; i++) { if (arr2[arr[i]]) { arr2[arr[i]] += 1; } else { arr2[arr[i]] = 1; } } console.log(arr2); var forsort = []; for (var item in arr2) forsort.push([item, arr2[item]]) forsort.sort( function(a, b) { return b[1] - a[1] }) for (var z = 0; z < forsort.length; z++) { arr3[forsort[z][0]] = forsort[z][1]; } console.log(arr3); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <span id="arr1"></span> 

  • Thank you, but now you can somehow sort the keys of this object by the number of repetitions? - Alexander
  • so you essentially didn’t change anything in the author’s decision)) but didn’t even sort anything - ampawd
  • @ Alexander corrected ... - C.Raf.T

As a result, the working code:

 var arr = ["aa", "aa", "ab", "ab", "ac", "a", "s", "s", "s"]; var resultReduce = arr.reduce(function(acc, cur) { if (!acc.hash[cur]) { acc.hash[cur] = { [cur]: 1 }; acc.map.set(acc.hash[cur], 1); acc.result.push(acc.hash[cur]); } else { acc.hash[cur][cur] += 1; acc.map.set(acc.hash[cur], acc.hash[cur][cur]); } return acc; }, { hash: {}, map: new Map(), result: [] }); var result = resultReduce.result.sort(function(a, b) { return resultReduce.map.get(b) - resultReduce.map.get(a); }); console.log(result); 

But now it is necessary that the result be displayed in any place on the screen indicating the number of repetitions. I can only output the actual values ​​of the array, for some reason the number is not displayed?

     var arr = ["aa", "aa", "ab", "ab", "ac", "a", "s"], counts = {}, res = []; for (var i in arr) { counts[arr[i]] = (counts[arr[i]] || 0) + 1; } Object.keys(counts).sort(function(a, b) { return counts[b] - counts[a] }).forEach(function(el, idx, arr) { res.push([el, counts[el]]); }); console.log(res); 

    • yours is sorted incorrectly - it is necessary descending res[b] - res[a] - ampawd
    • number of repetitions is not displayed - Grundy
    • Thanks, corrected, with the counters a bit more confusing, but you can choose the structure of the result as you like. As for me, this option is still more readable =) - InvDeath