0

I'm trying to sort an object so it doesn't have to do the same loop n times.

Given the following object

movies = [
    { 
        title: "The Lord of the Rings: The Fellowship of the Ring"
        year: 2001
    }, 
    { 
        title: "The Lord of the Rings: The Two Towers"
        year: 2002
    },
    { 
        title: "The Lord of the Rings: The Return of the King"
        year: 2003
    },
    { 
        title: "A Beautiful Mind"
        year: 2001
    },
]

I want the movies to be sorted by year and to appear on the screen:

Year 2003
- The Lord of the Rings: The Return of the King

Year 2002
- The Lord of the Rings: The Two Towers

Year 2001
- A Beautiful Mind
- The Lord of the Rings: The Fellowship of the Ring

To do this in vue I can do something like define an object years = [2003, 2002, 2001] and then

<div v-for="y in years">
    {{ y }}
    <div v-for="m in movies">
        <div v-if="m.year == y">
            {{ m.title }}
        </div>
    </div>
</div>

However in this way I repeat the v-for of movies for the length of the array years.

So I thought about organizing movies object in the following way:

moviesByYear = [
    2003: [
        { 
            title: "The Lord of the Rings: The Return of the King"
            year: 2003
        }
    ], 
    2002: [
        {
            title: "The Lord of the Rings: The Two Towers"
            year: 2002    
        }
    ],
    2001: [
        {
            title: "A Beautiful Mind"
            year: 2001
        },
        {
            title: "The Lord of the Rings: The Fellowship of the Ring"
            year: 2001
        }
    ]
]

That way I could use

<div v-for="(movies, year) in moviesByYear" :key="year">
    <div>{{ year }}</div>
    <div v-for="m in movies">
        {{ m.title }}
    </div>
</div>

Unfortunately, I can't build the moviesByYear array and I'm not sure it's the correct approach because there are drawbacks to sorting an object by keys. In fact I would need to sort the films both by ascending and descending year.

How could I solve this problem? Is there a better approach than running v-for n times?

Leonardo
  • 2,439
  • 33
  • 17
  • 31
  • ... and you _don't_ want to preprocess the `movies` array to consolidate years and sort is ascending order? – Kinglish Feb 24 '22 at 17:59
  • Thanks for your interest! Unfortunately I'm not experienced enough to understand what you mean, but I can do whatever is more efficient than doing a repeating loop. The only thing is that the starting point is `movies` object and the descending order is preferred over the ascending order – Leonardo Feb 24 '22 at 18:07
  • Does this answer your question? [How can I group an array of objects by key?](https://stackoverflow.com/questions/40774697/how-can-i-group-an-array-of-objects-by-key) – Heretic Monkey Feb 24 '22 at 18:27
  • I think it doesn't answer my question because it seems to me that it is not shown how to order the object – Leonardo Feb 24 '22 at 18:38

1 Answers1

1

I might be misunderstanding, but first you need to create an array of objects containing year => movie relationships. You can create the reverse sort (ascending) simply by reversing the array. This way you can use a simple iterable v-for, like

<div v-for="year in movieList" >
  <div>{{ year.year }}</div>
  <div v-for="m in year.movies">
    {{ m }}
  </div>
</div>

let movies = [{
    title: "The Lord of the Rings: The Fellowship of the Ring",
    year: 2009
  },
  {
    title: "The Lord of the Rings: The Two Towers",
    year: 2002
  },
  {
    title: "The Lord of the Rings: The Return of the King",
    year: 2003
  },
  {
    title: "A Beautiful Mind",
    year: 2009
  },
]

let movieList = movies.reduce((b, a) => {
  let index = b.findIndex(f => f.year === a.year);
  if (index < 0) b.push({
    year: a.year,
    movies: [a.title]
  });
  else b[index].movies.push(a.title);
  return b;
}, []).sort((a, b) => b.year - a.year);

console.log(movieList)
console.log('reversed: ', movieList.reverse())
Kinglish
  • 23,358
  • 3
  • 22
  • 43
  • Thank you very much! For now I can't reproduce your example on my real data, but it's a great solution. As soon as I resolve I accept the answer, in the meantime upvote it. – Leonardo Feb 24 '22 at 18:48
  • I had a typo in my v-for loop. {{ m }} -- not {{ m.title }} -- I updated the answer – Kinglish Feb 24 '22 at 18:50