Как я могу сопоставить массив объектов с массивом с идентификатором для повторяющихся значений? - PullRequest
0 голосов
/ 16 января 2019

Работа с массивом объектов, например:

const data = [
  {count: 400, value: "Car Wash Drops"},
  {count: 48, value: "Personal/Seeding"},
  {count: 48, value: "Personal/Seeding"},
];

Я хочу map для массива с дополнительным идентификатором для повторяющихся значений:

const expected = [
  ["Car Wash Drops", 400],
  ["Personal/Seeding (1)", 48],
  ["Personal/Seeding (2)", 48],
];

До сих пор у меня есть функция map для сопоставления значений соответственно, но я не уверен, как продолжить добавление идентификатора только для дубликатов.

data.map(d => [`${d.value}`, d.count]);

Результат:

[
  ["Car Wash Drops", 400],
  ["Personal/Seeding", 48],
  ["Personal/Seeding", 48],
]

Я также использовал индекс, но он добавляет индекс к каждому значению:

data.map((d, i) => [`${d.value} ${i}`, d.count]);

Результат:

[
  ["Car Wash Drops (0)", 400],
  ["Personal/Seeding (1)", 48],
  ["Personal/Seeding (2)", 48],
]

Ответы [ 4 ]

0 голосов
/ 21 января 2019

Ответ уже был дан, при всем уважении к автору ответа, я утверждаю, что в ответ могут быть внесены некоторые улучшения как в синтаксическом, так и в функциональном отношении:

  1. reduce следует избегать, если источник очень большой, хотя я всегда предпочитаю reduce, если источник относительно мал (data.length <1000).Попробуйте использовать <em>POF (обычный старый для :)) , поскольку это самый быстрый вариант.

  2. Карта ES6 является хорошим помощником, когда мы имеем дело с ключ-значение пары, но я предпочитаю POO (простой старый объект:) ), если это возможно, и это сделает наш код более эстетичным.

Я предоставлю свое решение проблемы (в худшем случае он запустит O (n) и использует дополнительное пространство O (n) для пар ключ-значение, он выполнит два прохода на источнике, поэтому второй проход необходим дляпоместите 1 там, где мы пропустили, когда мы впервые встретились с ним на первом проходе):

let data =  [
  {count: 400, value: "Car Wash Drops"},
  {count: 48, value: "Personal/Seeding"},
  {count: 48, value: "Personal/Seeding"},
  {count: 300, value: "Operators/Management"},
  {count: 48, value: "Personal/Seeding"}
];

const map = {};

for (let i=0;i<data.length;i+=1) {
  map[ data[i].value ] = map[ data[i].value ]+1 || 0;
  data[i].value = map[data[i].value]?data[i].value+` (${map[data[i].value]+1})`:data[i].value; 
}

for (let i=0;i<data.length;i+=1) {
  data[i].value = map[data[i].value]?data[i].value+` (1)`:data[i].value; 
}

console.log(data.map(o=>[o.value,o.count]));

или более краткая версия с использованием оператора ES6 of [ссылка на] :

let data =  [
      {count: 400, value: "Car Wash Drops"},
      {count: 48, value: "Personal/Seeding"},
      {count: 48, value: "Personal/Seeding"},
      {count: 300, value: "Operators/Management"},
      {count: 48, value: "Personal/Seeding"}
    ];

    const map = {};

    for (let d of data) {
      map[d.value] = map[d.value]+1 || 0;
      d.value = map[d.value]?d.value+` (${map[d.value]+1})`:d.value; 
    }

    for (let d of data) {
      d.value = map[d.value]?d.value+` (1)`:d.value; 
    }

    console.log(data.map(o=>[o.value,o.count]));
0 голосов
/ 16 января 2019

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

const data = [
  {count: 400, value: "Car Wash Drops"},
  {count: 48, value: "Personal/Seeding"},
  {count: 48, value: "Personal/Seeding"},
];

let valueCounts = data.reduce((a, c) => {
  a[c.value] = a[c.value] || {current: 1, total: 0};
  a[c.value].total += 1;
  return a;
}, {});

const expected = data.map(({count, value}) => {
  if (valueCounts[value].total === 1) return [value, count];
  return [`${value} (${valueCounts[value].current++})`, count];
});

console.log(expected);
0 голосов
/ 16 января 2019

Используя ваш подход, вы можете использовать filter() внутри карты, чтобы проверить, сколько элементов в исходном массиве имеют одинаковое значение текущего анализируемого, используя это условие, вы можете выбрать, что возвращать в качестве нового значения:

const data = [
  {count: 400, value: "Car Wash Drops"},
  {count: 48, value: "Personal/Seeding"},
  {count: 48, value: "Personal/Seeding"},
];

let res = data.map((x, idx) =>
{
    if (data.filter(y => y.value === x.value).length > 1)
        return [`${x.value} (${idx})`, x.count];
    else
        return [`${x.value}`, x.count];
});

console.log(res);

Производительность предыдущего подхода может быть улучшена, если мы используем some() вместо filter(), например:

const data = [
  {count: 400, value: "Car Wash Drops"},
  {count: 48, value: "Personal/Seeding"},
  {count: 48, value: "Personal/Seeding"},
  {count: 300, value: "Operators/Management"},
  {count: 48, value: "Personal/Seeding"}
];

let res = data.map((x, idx) =>
{
    if (data.some((y, j) => y.value === x.value && idx !== j))
        return [`${x.value} (${idx})`, x.count];
    else
        return [`${x.value}`, x.count];
});

console.log(res);

И может быть улучшено еще больше, если мы ранее создадим Map со счетчиком времени, в течение которого элемент появляется в исходном массиве. Как это:

const data = [
  {count: 400, value: "Car Wash Drops"},
  {count: 48, value: "Personal/Seeding"},
  {count: 48, value: "Personal/Seeding"},
  {count: 300, value: "Operators/Management"},
  {count: 48, value: "Personal/Seeding"}
];

let counters = data.reduce((res, {value}) =>
{
    res.set(value, res.has(value) ? res.get(value) + 1 : 1);
    return res;
}, new Map());

let res = data.map((x, idx) =>
{
    return [
        `${x.value}` + (counters.get(x.value) > 1 ? `(${idx})` : ""),
        x.count
    ]; 
});

console.log(res);
0 голосов
/ 16 января 2019

Вы можете передать второй аргумент .map() после обратного вызова. Это значение будет использоваться в качестве значения this при вызове обратного вызова. Таким образом, вы можете пройти через объект, который вы можете использовать для накопления количества повторений:

data.map(function(d, i) {
  if (!(d.value in this))
    this[d.value] = 0;
  else
    this[d.value] += 1;

  return [
    d.value + (this[d.value] ? " (" + this[d.value] + ")" : ""),
    d.count
  ];
}, {});

Начиная с пустого объекта, обратный вызов может отслеживать, сколько раз он просматривал каждую строку d.value. Когда он видит повтор, он может добавить к строке квалификатор.

Теперь, это не делает точно то, что вы просили, потому что оно смотрит только на одно значение за раз. Таким образом, в первый раз, когда он видит "Personal/Seeding", он не знает, что это дубликат, поэтому он не изменяется с помощью квалификатора. Второй раз через это, конечно.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...