Search code examples
javascriptvue.jsvuejs2javascript-objects

Iterate an object with reverse sorted keys


I know my question title can be confusing (I'm aware object properties don't have an order) since I'm already proposing a solution with it, so to avoid creating an XY problem, let me first explain my goal:

I need to render N tables of words grouped by word length and (this is the tricky thing) ordered by length in descending order . Something like:

Words with length 4
===================
abcd | other fields
abce | other fields 
abcf | other fields

Words with length 3
===================
abc  | other fields
abd  | other fields
abe  | other fields

Words with length 2
===================
...

I'm getting my list of words from an API without any grouping, so what I'm doing right now is:

let grouped = {};
// Assume words is an Array of words of multiple lengths
words.forEach(w => {
    if (typeof grouped[w.length] === 'undefined')
        grouped[w.length] = [];
    grouped[w.length].push({'word': w}); 
    // Pushing an object cause of other calculated and not rellevant fields
});

Of course, when I render this grouped words (I'm using Vue.js)...

<WordsTable 
    v-for="(group, length) in grouped"
    :words="group"
    :word-length="length"
/>

I use the word-length prop to render the table's header saying Words with length N.

Everything works but I get the tables in ascending order (and, again, I'm aware this might be a coincidence because there's no order in objects). So the real question is, can anybody figure out a way so that Vue iterates my grouped object with descending sorted keys?

Note: There might not be words of some specific lengths, so maybe Object.keys(grouped) is [3,4,7,9,10]

UPDATE:

A commenter suggests using an Array instead of an Object for grouped. I already tried and it's true that might be slightly better but doesn't completely solve the problem.

If grouped is an array, I can reverse it:

<WordsTable 
    v-for="(group, length) in grouped.reverse()"
    ...
/>

But in this case, indexes are lost and can no longer be used to render the table's header title.


Solution

  • You can create a computed property that sorts your object keys in a descending order:

    sortedGroupKeys() { 
        return Object.keys( group ).sort( ( a , b ) => b - a ); 
    }
    

    then you can loop though it.

    <WordsTable 
        v-for="(key in sortedGroupKeys)"
        :words="group[ key ]"
        :word-length="key"
    />
    

    Hope this helps.