Сложнее, чем необходимо: сравнение и подсчет элементов в массивах

У меня есть группа массивов, которые представляют категории. Каждый массив является категорией, и каждый элемент в массиве является субъектом, например

4 Categories containing Subjects
['A','B','D']
['C']
['E','F']
['G','H','I','J']

У меня есть другая группа массивов. Каждый из этих предметов имеет до четырех возможных предметов, например

3 Items containing Subjects
['A','F']
['E','I','C']
['E','F','G']

Я хочу подсчитать количество предметов для каждой категории. В этом случае результаты должны быть:

Total Items: 3
Category 1: 1
Category 2: 1
Category 3: 3
Category 4: 2

3 элемента, некоторые из которых находятся в нескольких категориях. Некоторые из моих предметов имеют два предмета в одной категории, и именно здесь я ошибаюсь. Мои результаты тогда:

Total Items: 3
Category 1: 1
Category 2: 1
Category 3: 4
Category 4: 2

Мой счет для категории 3 отключен на 1, потому что мой третий пункт имеет два предмета в той же категории, E и F.

Что я пробовал

Для справки, категории-это массив объектов:

categories = [
  { name: string, subjects: string[], count: number }
]

Предметы несколько похожи:

items = [
  { subjects: Subject[] }
]

А тема просто:

{ id: string, name: string }

Это то, что мне нужно настроить:

categories.map(category => 
  category.subjects.map(categorySubject => {
    if(items.subjects.map(itemSubject => itemSubject.id)
      .some(val => itemSubject.indexOf(val) === 0)) {
        category.count++;
    }
  }));

Я думал, что я пошел в правильном направлении с некоторыми, но мне нужно найти способ, чтобы оба проверить, если предметные предметы находятся в категориях, не считая дважды для предметов, которые имеют несколько предметов в категории. Я знаю, что проблема в моем подходе. Я не могу изменить способ форматирования элементов (да, идентификаторы действительно являются строками), но я мог бы изменить категории, если есть лучший способ.

2 ответа

  1. Чтобы ответить на ваш вопрос, это, кажется, работает:

    var categories: Array<{ name: string, subjects: Subject[], count: number }> = [];
    
    type Subject = { id: string, name: string }
    
    var items: Array<{ subjects: Subject[] }> = [];
    
    function inItem(subject: Subject, item: { subjects: Subject[] }): boolean {
        return item.subjects.some(itemSubject => itemSubject.id === subject.id);
    }
    
    categories.forEach((category, index) => {
        let count = 0;
    
        for (let j = 0; j < items.length; j++) {
            for (let i = 0; i < category.subjects.length; i++) {
                if (inItem(category.subjects[i], items[j])) {
                    count++;
                    break;
                }
            }
        }
    
        console.log(`Category ${ index + 1 }: ${ count }`);
    });
    

    (код с данными в playground)

    Но это не очень хорошее решение, так как это не очень легко понять, если вы не тот, кто написал код, или вы через 2 месяца.
    Вероятно, вы должны придумать лучшее решение, например, индексировать его.

    Just have a CategoriesIndexwhich holds all of the categories/subjects and through it you add/remove/change them. В нем вы можете иметь те же массивы, но также ссылки из элементов в категории.

  2. Это то, что я придумал, но никто не сможет сразу понять, что происходит, когда они это увидят.

    categories.map(category => category.count = 0);
    let nextCategory = false;
    let itemSubjects = items.map(item => item.subjects)
      .map(subjects => subjects.map(subject => subject.id));
    for(var i = 0; i < items.length; i++){
      for(var j = 0; j < categories.length; j++){
        nextCategory = false;
        for(var k = 0; k < categories[j].subjects.length; k++){
          for(var l = 0; l < itemSubjects[i].length; l++){
            if(itemSubjects[i][l] === categories[j].subjects[k]){
              categories[j].count++;
              nextCategory = true;
              break;
            }
          }
          if(nextCategory === true){
            break;
          }
        }
      }
    }