如何将对象数组映射到带有重复值标识符的数组?

时间:2019-01-16 16:52:00

标签: javascript arrays object

使用对象数组,如:

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],
];

到目前为止,我有一个映射函数来映射相应的值,但是不确定如何继续仅对重复项附加标识符。

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 :(得分:3)

使用您的方法,您可以在地图内使用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);

答案 1 :(得分:3)

已经给出了答案,在充分尊重答案作者的情况下,我认为可能在语法和性能上都可以对答案进行一些改进:

    如果源很大,应避免使用
  1. reduce,但是如果源相对较小(data.length <1000),我总是更喜欢reduce。尝试使用 POF(原为:)),因为它是最快的选择。

  2. 当我们处理 key-value 对时,ES6 Map是一个很好的助手,但是我更喜欢 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运算符[link to 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]));

答案 2 :(得分:0)

您可以维护从看到的值到看到的值之间的映射。然后,根据是否重复,更容易获得正确的号码:

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);

答案 3 :(得分:-1)

您可以在回调之后将第二个参数传递给.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"时就不知道它是重复项,因此不会被限定符修改。当然是第二次。

如果这还不够好,请执行您要求的操作,首先需要对数组进行完整遍历,以获得每个字符串的重复项最终计数。