Фильтровать массив объектов по вложенным значениям между каждым элементом - PullRequest
1 голос
/ 07 августа 2020

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

Мне нужно выбрать элемент с наивысшим currentSeason.endDate для каждого competition.id, которые одинаковы.

Вход:

[
  {
    "competition": {
      "id": 2015,
    },
    "currentSeason": {
      "id": 177, "endDate": "2019-05-25",
    },
    "id": "5b8e6ba74178bc111c9b649e"
  },
  {
    "competition": {
      "id": 2015,
    },
    "currentSeason": {
      "id": 499, "endDate": "2020-05-31",
    },
    "id": "5d4191576a32da53f0a57c70"
  },
  {
    "competition": {
      "id": 2084,
    },
    "currentSeason": {
      "id": 508, "endDate": "2020-05-30",
    },
    "id": "5d42bad89dd17c0ffccff9f8"
  },
  {
    "competition": {
      "id": 2013,
    },
    "currentSeason": {
      "id": 589, "endDate": "2020-12-06",
    },
    "id": "5e622421bcd3fd22b4b372df"
  },
  {
    "competition": {
      "id": 2015,
    },
    "currentSeason": {
      "id": 596, "endDate": "2021-05-23",1
    },
    "id": "5f16bd18da7e443c44cb7ca5"
  },
  {
    "competition": {
      "id": 2084,
    },
    "currentSeason": {
      "id": 603, "endDate": "2021-05-30",
    },
    "id": "5f16bd76da7e443c44cb7ca7"
  },
  {
    "competition": {
      "id": 2011,
    },
    "currentSeason": {
      "id": 473, "endDate": "2020-07-04",
    },
    "id": "5d42ba929dd17c0ffccff9f7"
  },
]

Желаемый результат:

[
  {
    "competition": {
      "id": 2013,
    },
    "currentSeason": {
      "id": 589, "endDate": "2020-12-06",
    },
    "id": "5e622421bcd3fd22b4b372df"
  },
  {
    "competition": {
      "id": 2015,
    },
    "currentSeason": {
      "id": 596, "endDate": "2021-05-23",1
    },
    "id": "5f16bd18da7e443c44cb7ca5"
  },
  {
    "competition": {
      "id": 2084,
    },
    "currentSeason": {
      "id": 603, "endDate": "2021-05-30",
    },
    "id": "5f16bd76da7e443c44cb7ca7"
  },
  {
    "competition": {
      "id": 2011,
    },
    "currentSeason": {
      "id": 473, "endDate": "2020-07-04",
    },
    "id": "5d42ba929dd17c0ffccff9f7"
  },
]

Ближайшее, что я получил, это два вложенных foreach, но по какой-то причине все еще есть дубликаты:

competitions.forEach((c, i1) => {
  competitions.forEach((n, i2) => {
    if (c.competition.id === n.competition.id && c.id !== n.id) {
      const cEndDate = new Date(c.currentSeason.endDate);
      const nEndDate = new Date(n.currentSeason.endDate);

      if (cEndDate < nEndDate) {
        competitions.splice(i1, 1);
      } else {
        competitions.splice(i2, 1);
      }
    }
  });
});

splice, так как нужно переиндексировать длину массива, но поскольку я получил эти вложенные foreach, там может быть какая-то проблема. Я также чувствую, что метод filter может быть полезен с исходным массивом, передаваемым в качестве параметра, но застрявшим на logi c. Заранее спасибо.

Вот код для тестирования: https://codepen.io/kevinch/pen/dyMPdww

Ответы [ 3 ]

2 голосов
/ 07 августа 2020

Самый простой способ сделать это - уменьшить количество соревнований внутри карты и вернуть значения.

Просто сравните текущую и предыдущую даты, сохранив более позднюю.

const latestCompDateById = (data) => {
  return [...data.reduce((idMap, curr) => {
    let prev = idMap.get(curr.competition.id);
    if (prev != null) {
      let prevDate = new Date(prev.currentSeason.endDate),
          currDate = new Date(curr.currentSeason.endDate);
      if (currDate > prevDate) {
        idMap.set(curr.competition.id, curr);
      }
    } else {
      idMap.set(curr.competition.id, curr);
    }
    return idMap;
  }, new Map()).values()];
}

console.log(latestCompDateById(getData()));

function getData() {
  return [{
    "competition": {
      "id": 2015
    },
    "currentSeason": {
      "id": 177,
      "endDate": "2019-05-25"
    },
    "id": "5b8e6ba74178bc111c9b649e"
  }, {
    "competition": {
      "id": 2015
    },
    "currentSeason": {
      "id": 499,
      "endDate": "2020-05-31"
    },
    "id": "5d4191576a32da53f0a57c70"
  }, {
    "competition": {
      "id": 2084
    },
    "currentSeason": {
      "id": 508,
      "endDate": "2020-05-30"
    },
    "id": "5d42bad89dd17c0ffccff9f8"
  }, {
    "competition": {
      "id": 2013
    },
    "currentSeason": {
      "id": 589,
      "endDate": "2020-12-06"
    },
    "id": "5e622421bcd3fd22b4b372df"
  }, {
    "competition": {
      "id": 2015
    },
    "currentSeason": {
      "id": 596,
      "endDate": "2021-05-23"
    },
    "id": "5f16bd18da7e443c44cb7ca5"
  }, {
    "competition": {
      "id": 2084
    },
    "currentSeason": {
      "id": 603,
      "endDate": "2021-05-30"
    },
    "id": "5f16bd76da7e443c44cb7ca7"
  }, {
    "competition": {
      "id": 2011
    },
    "currentSeason": {
      "id": 473,
      "endDate": "2020-07-04"
    },
    "id": "5d42ba929dd17c0ffccff9f7"
  }];
}
.as-console-wrapper { top: 0; max-height: 100% !important }

Возможность повторного использования

Если вам нужна более надежная и универсальная версия c, вы можете попробовать это.

Это позволяет:

  • Указывать, что вводить
  • Как вы сравниваете
  • Необязательно сортировать по клавишам

const groupReduce = (data, config) => {
  const opts = {
    keyFn: (item) => item.id,
    cmpFn: (curr, prev) => curr - prev > 0,
    sort: false,
    ...config
  };
  const result = [...data.reduce((idMap, curr) => {
    const key = opts.keyFn(curr), prev = idMap.get(key);
    if (prev != null) {
      if (opts.cmpFn(curr, prev)) idMap.set(key, curr)
    } else idMap.set(key, curr);
    return idMap;
  }, new Map()).values()];
  return opts.sort ? result.sort((left, right) => {
    return opts.keyFn(left) - opts.keyFn(right);
  }) : result;
}

console.log(groupReduce(getData(), {
  keyFn: (item) => item.competition.id,
  cmpFn: (curr, prev) => {
    const currDate = new Date(curr.currentSeason.endDate),
          prevDate = new Date(prev.currentSeason.endDate);
    return currDate - prevDate > 0;
  },
  sort: true
}));

function getData() {
  return [{
    "competition": {
      "id": 2015
    },
    "currentSeason": {
      "id": 177,
      "endDate": "2019-05-25"
    },
    "id": "5b8e6ba74178bc111c9b649e"
  }, {
    "competition": {
      "id": 2015
    },
    "currentSeason": {
      "id": 499,
      "endDate": "2020-05-31"
    },
    "id": "5d4191576a32da53f0a57c70"
  }, {
    "competition": {
      "id": 2084
    },
    "currentSeason": {
      "id": 508,
      "endDate": "2020-05-30"
    },
    "id": "5d42bad89dd17c0ffccff9f8"
  }, {
    "competition": {
      "id": 2013
    },
    "currentSeason": {
      "id": 589,
      "endDate": "2020-12-06"
    },
    "id": "5e622421bcd3fd22b4b372df"
  }, {
    "competition": {
      "id": 2015
    },
    "currentSeason": {
      "id": 596,
      "endDate": "2021-05-23"
    },
    "id": "5f16bd18da7e443c44cb7ca5"
  }, {
    "competition": {
      "id": 2084
    },
    "currentSeason": {
      "id": 603,
      "endDate": "2021-05-30"
    },
    "id": "5f16bd76da7e443c44cb7ca7"
  }, {
    "competition": {
      "id": 2011
    },
    "currentSeason": {
      "id": 473,
      "endDate": "2020-07-04"
    },
    "id": "5d42ba929dd17c0ffccff9f7"
  }];
}
.as-console-wrapper { top: 0; max-height: 100% !important }
0 голосов
/ 07 августа 2020

Эту проблему можно решить за 2 шага.

  1. Сгруппируйте свои объекты по идентификатору конкурса.
  2. Получите объект с самой высокой датой окончания текущего сезона из каждой группы.

const objects = [{"competition":{"id":2015},"currentSeason":{"id":177,"endDate":"2019-05-25"},"id":"5b8e6ba74178bc111c9b649e"},{"competition":{"id":2015},"currentSeason":{"id":499,"endDate":"2020-05-31"},"id":"5d4191576a32da53f0a57c70"},{"competition":{"id":2084},"currentSeason":{"id":508,"endDate":"2020-05-30"},"id":"5d42bad89dd17c0ffccff9f8"},{"competition":{"id":2013},"currentSeason":{"id":589,"endDate":"2020-12-06"},"id":"5e622421bcd3fd22b4b372df"},{"competition":{"id":2015},"currentSeason":{"id":596,"endDate":"2021-05-23"},"id":"5f16bd18da7e443c44cb7ca5"},{"competition":{"id":2084},"currentSeason":{"id":603,"endDate":"2021-05-30"},"id":"5f16bd76da7e443c44cb7ca7"},{"competition":{"id":2011},"currentSeason":{"id":473,"endDate":"2020-07-04"},"id":"5d42ba929dd17c0ffccff9f7"}];

const groupedObjects = groupBy(objects, obj => obj.competition.id);
const chunkedObjects = Array.from(groupedObjects.values());
const maxObjects = chunkedObjects.map(chunk => (
  maxBy(chunk, obj => obj.currentSeason.endDate)
));

console.log(maxObjects);


function groupBy(iterable, keyFn) {
  const groups = new Map();
  for (const value of iterable) {
    const key = keyFn(value);
    if (!groups.has(key)) groups.set(key, []);
    groups.get(key).push(value);
  }
  return groups;
}

function maxBy(iterable, keyFn) {
  const iterator = iterable[Symbol.iterator]();
  const {done, value} = iterator.next();
  if (done) return;
  
  let max = {key: keyFn(value), value};
  for (const value of iterator) {
    const key = keyFn(value);
    if (key > max.key) max = {key, value};
  }
  return max.value;
}
0 голосов
/ 07 августа 2020

Вы должны иметь возможность использовать Array.reduce для преобразования данных в желаемую форму.

Если запись в Competition.id пуста, или имеет более раннюю дату окончания, замените ее.

let data = [ { "competition": { "id": 2015, }, "currentSeason": { "id": 177, "endDate": "2019-05-25", }, "id": "5b8e6ba74178bc111c9b649e" }, { "competition": { "id": 2015, }, "currentSeason": { "id": 499, "endDate": "2020-05-31", }, "id": "5d4191576a32da53f0a57c70" }, { "competition": { "id": 2084, }, "currentSeason": { "id": 508, "endDate": "2020-05-30", }, "id": "5d42bad89dd17c0ffccff9f8" }, { "competition": { "id": 2013, }, "currentSeason": { "id": 589, "endDate": "2020-12-06", }, "id": "5e622421bcd3fd22b4b372df" }, { "competition": { "id": 2015, }, "currentSeason": { "id": 596, "endDate": "2021-05-23", }, "id": "5f16bd18da7e443c44cb7ca5" }, { "competition": { "id": 2084, }, "currentSeason": { "id": 603, "endDate": "2021-05-30", }, "id": "5f16bd76da7e443c44cb7ca7" }, { "competition": { "id": 2011, }, "currentSeason": { "id": 473, "endDate": "2020-07-04", }, "id": "5d42ba929dd17c0ffccff9f7" },]

let result = data.reduce((output, el) => {
    let latestEntryIndex = output.findIndex(r => r.competition.id === el.competition.id);
    if (latestEntryIndex < 0) {
        // There is no entry for the given competition, add it.
        output.push(el)
    } else if (output[latestEntryIndex].currentSeason.endDate < el.currentSeason.endDate) {
        // The entry has an older endDate, replace it
        output[latestEntryIndex] = el;
    }
    return output;
}, []);

console.log("Result:", result);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...