Как отфильтровать массив с объектами по значениям во вложенном массиве и подсчитать их - PullRequest
0 голосов
/ 23 декабря 2018

У меня есть фильмы array с objects списком жанров (как array) для каждого фильма:

const movies = [
    {
        "title": "Movie A",
        "genre": ["Action", "Sci-Fi", "Thriller"]
    },
    {
        "title": "Movie B",
        "genre": ["Horror, Sci-Fi"]
    },
    {
        "title": "Movie C",
        "genre": ["Action", "Horror", "Thriller"]
    },
    {
        "title": "Movie D",
        "genre": ["Mystery", "Horror", "Sci-Fi"]
    }
];

Использование (vanilla, ES6 + или Lodash)JavaScript: Как мне создать новый array (см. Ниже) с objects, показывающим, сколько раз (count ниже) жанра (label ниже) указан выше фильмы genre массив?

Другими словами: сколько раз жанр указан выше.

Конечный результат : новый array отсортированный по алфавиту label:

const genres = [
    {
        "label": "Action",
        "count": 2
    },
    {
        "label": "Horror",
        "count": 3
    },
    {
        "label": "Mystery",
        "count": 1
    },
    {
        "label": "Sci-Fi",
        "count": 3
    },
    {
        "label": "Thriller",
        "count": 2
    }
];

Ответы [ 3 ]

0 голосов
/ 23 декабря 2018

Это создаст массив с метками и количеством

const movies = [
        {
            "title": "Movie A",
            "genre": ["Action", "Sci-Fi", "Thriller"]
        },
        {
            "title": "Movie B",
            "genre": ["Horror", "Sci-Fi"]
        },
        {
            "title": "Movie C",
            "genre": ["Action", "Horror", "Thriller"]
        },
        {
            "title": "Movie D",
            "genre": ["Mystery", "Horror", "Sci-Fi"]
        }
    ];
    var genres = [];
    for (var i = 0; i < movies.length; i++) {
        var list = movies[i].genre;
        for (var j = 0; j < list.length; j++) {
    
            var existingValue = genres.find(function (value) {
                return value.label === list[j]
            });
    
            if (!existingValue) {
                genres.push(
                    {
                        label: list[j],
                        count: 1
                    }
                );
            } else {
                existingValue.count++;
            }
        }
    }
    console.log(genres)
0 голосов
/ 23 декабря 2018
  • Получить все genres в массив строк, используя reduce
  • sort массив строк
  • Используйте reduce, чтобы получить конечный объект, который вам нужен
    • Внутри этого проверьте, есть ли у объекта counter значение label для текущего genre
    • , если да, приращениеэто, иначе добавить новый объект в массив

const movies=[{title:"Movie A",genre:["Action","Sci-Fi","Thriller"]},{title:"Movie B",genre:["Horror","Sci-Fi"]},{title:"Movie C",genre:["Action","Horror","Thriller"]},{title:"Movie D",genre:["Mystery","Horror","Sci-Fi"]}];

const final = movies.reduce((genres, {genre}) => genres.concat(genre), [])
  .sort()
  .reduce((counter, genre) => {
    const item = counter.find(c => c.label === genre);
    item ? item["count"]++ : counter.push({ label:genre, count:1 });
    return counter
  }, []);

console.log(final);

Или вы можете создать объект с каждым genre в качестве ключа и значением в качестве объекта, который вам нужен в конечном массиве.Затем используйте Object.values, чтобы получить желаемый результат:

const movies=[{title:"Movie A",genre:["Action","Sci-Fi","Thriller"]},{title:"Movie B",genre:["Horror","Sci-Fi"]},{title:"Movie C",genre:["Action","Horror","Thriller"]},{title:"Movie D",genre:["Mystery","Horror","Sci-Fi"]}];

const final = movies
              .map(a => a.genre)
              .flat()
              .sort()
              .reduce((a, label) =>
                ((a[label] = a[label] || {label, count: 0})["count"]++,a), {});

console.log(Object.values(final));
0 голосов
/ 23 декабря 2018

Этого можно добиться, поместив все массивы genre в массив, а затем сгладив этот массив, используя .flat().После этого вы можете использовать .reduce для создания массива объектов из этого массива.

См. Рабочий пример ниже (см. Комментарии к коду для дальнейшего объяснения):

const  movies= [{title:"Movie A",genre:["Action","Sci-Fi","Thriller"]},{title:"Movie B",genre:["Horror","Sci-Fi"]},{title:"Movie C",genre:["Action","Horror","Thriller"]},{title:"Movie D",genre:["Mystery","Horror","Sci-Fi"]}];

res = movies.map(({genre}) => genre) // create array of genres (multi-dimensonal)
        .flat() // flatten the array of arrays to only have genres in it
        .sort((a,b) => a.localeCompare(b)) // sort the array alphabetically
        .reduce((acc, genre) => {
          let i = acc.length-1 // get the previous index of the last object
          let prev = acc[i]; // get the previous object
          if(prev && prev.label == genre) { // if the previous label is equal to the curren genre than:
            acc[i].count++; // add one to the current objects count
          } else { // otherwise...
            acc = [...acc, {label: genre, count: 1}]; // append a new object to the accumilator
          }
          return acc; // return the result of the accumilator to be used in next iteration
        }, []); // set starting value of reduce to empty array

console.log(res);

В качестве альтернативы, если вы не можете позволить себе использовать метод .flat(), вы можете использовать следующее:

const  movies= [{title:"Movie A",genre:["Action","Sci-Fi","Thriller"]},{title:"Movie B",genre:["Horror","Sci-Fi"]},{title:"Movie C",genre:["Action","Horror","Thriller"]},{title:"Movie D",genre:["Mystery","Horror","Sci-Fi"]}];

res = [].concat.apply([], movies.map(({genre}) => genre))
        .sort((a,b) => a.localeCompare(b))
        .reduce((acc, genre) => {
          let i = acc.length-1
          let prev = acc[i];
          if(prev && prev.label == genre) {
            acc[i].count++;
          } else {
            acc = [...acc, {label: genre, count: 1}];
          }
          return acc;
        }, []);

console.log(res);
...