Можно ли отображать повторяющиеся записи по ключу как одну запись и назначать ей массив данных один-ко-многим

Модели

const Quest = sequelize.define('quest' , {
 name: {
   type: DataTypes.STRING,
   allowNull: false,
 }
});
const QuestPack = sequelize.define('quest_packs', {
 day: {
   type: DataTypes.INTEGER,
   allowNull: false,
 },
 questId: {
   type: DataTypes.INTEGER,
   allowNull: false,
 },
 videoId: {
   type: DataTypes.INTEGER,
   allowNull: false,
 }
});

const Video = sequelize.define('video', {
 url: {
   type: DataTypes.INTEGER,
   allowNull: false,
 }
});
Quest.belongsToMany(Video, {
  through: QuestPack,
});
Quest.hasMany(QuestPack, {
  foreignKey: 'challengeId',
});
QuestPack.belongsTo(Video, {
  foreignKey: 'videoId',
});
Video.belongsToMany(Quest, {
  foreignKey: 'videoId',
  through: QuestPack,
});

Запрос

const data = await Quest.findByPk(1, {
  include: [
    {
      model: QuestPack,
      attributes: ['day'],
      include: [{ model: Video }],
    }
  ],
});

Я получаю данные в таком формате:

{
  "id": 1,
  "name": "Name quest",
  "questPacks": [
    {
      "day": 1,
      "video": {
        "id": 1,
        "url": "youtube link",
      }
    },
    {
      "day": 1,
      "video": {
        "id": 2,
        "url": "youtube link",
      }
    }
  ]
}

Я хочу, чтобы все мои повторяющиеся записи по ключу day были удалены, и отображалась только одна запись, содержащая массив всех видео, принадлежащих questId.

{
  "id": 1,
  "name": "Name quest",
  "questPacks": [
    {
      "day": 1,
      "videos": [
        {
          "id": 1,
          "url": "youtube link",
        },
        {
          "id": 2,
          "url": "youtube link",
        }
      ]
    },
  ]
}

Можно ли это реализовать с sequelize ?


Ответы (1 шт):

Автор решения: Daniil Loban

через редьюс создаем новый массив questPacks, далее путем поиска совпадений по дню добавляем остальные видео.

const o = {
  "id": 1,
  "name": "Name quest",
  "questPacks": [
    {
      "day": 1,
      "video": {
        "id": 1,
        "url": "youtube link",
      }
    },
    {
      "day": 1,
      "video": {
        "id": 2,
        "url": "youtube link",
      }
    },
    {
      "day": 2,
      "video": {
        "id": 2,
        "url": "youtube link",
      }
    },
    {
      "day": 1,
      "video": {
         "id": 3,
         "url": "youtube link",
    }
},
  ]
}


function packDays(packs) {
  const result = {
    id: packs.id,
    name: packs.name,
    questPacks: []
  }

  result.questPacks = o.questPacks.reduce((acc, pack) => {
    let nextPack = null;
    if (nextPack = o.questPacks.find(days => days.day === pack.day) ){
      //  первый объект (видео по дню) помещаем в массив
      if(nextPack === pack) {
        nextPack.video = [nextPack.video]
        acc.push(pack)
        return acc
      }
      // при совпадении дней пушим их в один массив
      nextPack.video.push(pack.video);
      return acc
    }  
  }, [/* инициируем пустой массив */])
  return result
}

const inp = document.querySelector('#inp');
const out = document.querySelector('#out');
inp.textContent = JSON.stringify(o, null, 2)
out.textContent = JSON.stringify(packDays(o), null, 2)
<p><b>оригинал:</b></p>
<pre id="inp"></pre>
<p><b>получаем:</b></p>
<pre id="out"></pre>

→ Ссылка