1

I want to sort an array by element frequency. My code works for arrays of strings, but not for arrays of numbers:

const countOccurrences = (arr, val) => arr.reduce((a, v) => (v === val ? a + 1 : a), 0);

function frequencySort(arr){
  let d = {}
  arr.forEach(i => d[i] = countOccurrences(arr,i))
  arr.sort(function(a,b){
    return d[b] - d[a]
  })
  
  return arr
}



frequencySort(['a','b','b','b','c','c'])) returns [ 'b', 'b', 'b', 'c', 'c', 'a' ]

frequencySort([4, 6, 2, 2, 6, 4, 4, 4]) returns [ 4, 4, 4, 4, 6, 2, 2, 6 ]
3
  • Does this answer your question? Sort a Javascript Array by frequency and then filter repeats Commented Oct 14, 2021 at 18:30
  • @M4rs3l ... From all the provided solutions/approaches are there any questions left? Commented Oct 19, 2021 at 7:44
  • I am totally new here and forgot about this. Going through the answers now and everything is quite helpful. @PeterSeliger my upvotes are not counted yet, but they are recorded. Commented Jan 11, 2022 at 18:33

5 Answers 5

2

The only reason your letters worked is because you didn't have the same number of any two letters, where in your numbers, you have 2 of both 2 and 6.

Here's your snippet, but with 2 a's and 2 c's. You'll see it's out of order just like the numbers.

const countOccurrences = (arr, val) => arr.reduce((a, v) => (v === val ? a + 1 : a), 0);

function frequencySort(arr){
  let d = {}
  arr.forEach(i => d[i] = countOccurrences(arr,i))
  arr.sort(function(a,b){
    return d[b] - d[a]
  })
  
  return arr
}

console.log(frequencySort(['a','b','b','b','c','c', 'a']))

You need a way to sort instances that have the same number of occurrences. I adapted your forEach loop to give the last index of each letter to your b object and then changed your sort to use that index in case the number of occurrences is the same.

const countOccurrences = (arr, val) => arr.reduce((a, v) => (v === val ? a + 1 : a), 0);

function frequencySort(arr){
  let d = {}
  arr.forEach((i,index) => d[i] = {
    num: countOccurrences(arr,i),
    i: index
  });
  arr.sort(function(a,b){
    let diff = d[b].num - d[a].num;
    if(diff == 0)
      diff = d[b].i - d[a].i;
    return diff;
  })
  
  return arr
}

console.log(frequencySort(['a','b','b','b','c','c', 'a']))
console.log(frequencySort([4, 6, 2, 2, 6, 4, 4, 4]));

Sign up to request clarification or add additional context in comments.

Comments

0

It has nothing to do with the elements being letters or numbers. In you letters array, each letter has unique occurence count (3, 2, 1), therefore they are sorted the way you want to.

However, in your numbers array, "2" and "6" both occur 2 times each. Therefore, your sort callback function returns 0 for them, and they are treated as equal order by the sort function.

Comments

0

In your array of numbers you have the same amount of the number 2 as 6 and your sorting function doesn't care about the actual values it just cares about their counts. So in your example 2 and 6 both have the same priority.

You want to adjust your sorting function to compare values of elements if they have the same amount of occurrences. You'll need to implement separate comparisons for all the data types you want to accept and decide if you want ascending/descending order.

Here is a basic example for number and string elements:

const countOccurrences = (arr, val) => arr.reduce((a, v) => (v === val ? a + 1 : a), 0);

function frequencySort(arr){
  let d = {}
  arr.forEach(i => d[i] = countOccurrences(arr,i))
  arr.sort(function(a,b){
    const r = d[b] - d[a]
    if (r != 0) return r
    switch (typeof d[a]) {
    case 'number': return a - b
    case 'string': return a.localeCompare(b)
    default: return 0
    }
  })
  
  return arr
}



console.log(frequencySort(['a','b','b','b','c','c'])) // returns [ 'b', 'b', 'b', 'c', 'c', 'a' ]

console.log(frequencySort([4, 6, 2, 2, 6, 4, 4, 4])) // returns [ 4, 4, 4, 4, 2, 2, 6, 6 ]

Comments

0

A possible approach would first collect all equal array items within an item specific group array by a reduce task ...

console.log(
  "grouped ['a','b','b','b','c','c'] ...",
  ['a','b','b','b','c','c'].reduce((index, item) => {
    const groupList =
      index[`${ (typeof item) }_${ item }`] ??= [];

    groupList.push(item);

    return index;
  }, {})
);
console.log(
  "grouped [4, 6, 2, 2, 6, 4, 4, 4,'4','2','2'] ...",
  [4, 6, 2, 2, 6, 4, 4, 4,'4','2','2'].reduce((index, item) => {
    const groupList =
      index[`${ (typeof item) }_${ item }`] ??= [];

    groupList.push(item);

    return index;
  }, {})
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

The final computation then has to transform ... via Object.values ... the temporary result (as shown above) into an array of arrays of equal items where the former gets 1stly sorted by each array's length (indicates the items frequency) and 2ndly, for arrays of equal length', by a locale compare of each array's first item. The final result is the sorted array's flatted version ...

function sortItemsByFrequency(arr) {

  const groupedItems = arr.reduce((index, item) => {
    const groupList =
      index[`${ (typeof item) }_${ item }`] ??= [];

    groupList.push(item);

    return index;
  }, {});

  return Object
    .values(groupedItems)
    .sort((a, b) =>
      // - sort by frequency first indicated by an
      //   array's length.
      // - the higher frequency count wins.
      b.length - a.length ||
      // in case of equal frequency counts do a
      // locale compare of both array's first items.
      b[0].toLocaleString().localeCompare(a[0].toLocaleString())
    )
    .flat();
}

console.log(
  "sortItemsByFrequency(['a','b','b','b','c','c']) ...",
  sortItemsByFrequency(['a','b','b','b','c','c'])
);
console.log(
  "sortItemsByFrequency([4, 6, 2, 2, 6, 4, 4, 4,'4','2','2']) ...",
  sortItemsByFrequency([4, 6, 2, 2, 6, 4, 4, 4,'4','2','2'])
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

Comments

0

sort first based on frequency of characters in desc order,if freq is same, then sort alphabetically in asc order.

const str = 'zzzzvnttteeeqqaao';

const frequencySort = (str = '') => {
   let map = {}
   for (const letter of str) {
      map[letter] = (map[letter] || 0) + 1;
   };
   let res = "";
      
   let sorted = Object.keys(map).sort((a, b) => map[b] < map[a] ? -1 : 1);
//console.log(sorted);
 
   for (let letter of sorted) {
      for (let count = 0; count < map[letter]; count++) {
         res += letter
      }
   }
   return res;
};
console.log(frequencySort(str));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.