计算一个值在对象数组中出现的次数

Count how many times a value occurs in a array of objects

假设我有:

const map1 = [
  { id: 1, res: { a: `1-a`, b: `1-b` } },
  { id: 2, res: { a: `2-a`, b: `2-b`, c: `2-c`, d: `2-d` } },
  { id: 3, res: { a: `3-a`, b: `3-b`, c: `3-c` } }
]
const map2 = [
  { map1: { 1: 'a', 2: 'c', 3: 'b' } },
  { map1: { 1: 'b', 2: 'c', 3: 'a' } },
  { map1: { 1: 'a', 2: 'a' } },
  { map1: { 1: 'a', 2: 'a', 3: 'b' } },
  { map1: { 2: 'd',         3: 'a' } },
  { map1: { 1: 'a', 2: 'c', 3: 'c' } },
  { map1: { 1: 'b', 2: 'd', 3: 'c' } },
  { map1: { 1: 'b',         3: 'a' } }
]

这是我想要得到的结果:

const result = { 
  1: { a: 4, b: 3 }, 
  2: { a: 2, b: 0, c: 3, d: 1 }, 
  3: { a: 3, b: 2, c: 2 } 
}

result是一个对象,其键是map1id,值是包含累加和的对象。 例如:

result[2] = { a: 2, b: 0, c: 3, d: 1 } 因为在 map2.map1 中循环每个对象并使用键 2 查看值(2 因为我们正在寻找结果 [2])有 2 次 a、0 次 b、3 次 c 和 1 次 d

我想我必须使用 reduce 但是如何呢?对我来说似乎太复杂了.. 这是我的出发点:

const results = map2.reduce((accumulator, current) => {
  // ??  
  return accumulator
}, [])

您可以计算所有 key/value 对 map2 并从 map1 生成想要的结果。

对于每个给定的数组,此方法只需要 一个循环

const
    map1 = [{ id: 1, res: { a: `1-a`, b: `1-b` } }, { id: 2, res: { a: `2-a`, b: `2-b`, c: `2-c`, d: `2-d` } }, { id: 3, res: { a: `3-a`, b: `3-b`, c: `3-c` } }],
    map2 = [{ map1: { 1: 'a', 2: 'c', 3: 'b' } }, { map1: { 1: 'b', 2: 'c', 3: 'a' } }, { map1: { 1: 'a', 2: 'a' } }, { map1: { 1: 'a', 2: 'a', 3: 'b' } }, { map1: { 2: 'd', 3: 'a' } }, { map1: { 1: 'a', 2: 'c', 3: 'c' } }, { map1: { 1: 'b', 2: 'd', 3: 'c' } }, { map1: { 1: 'b', 3: 'a' } }],
    temp = map2.reduce((r, { map1 }) => {
        Object.entries(map1).forEach(e => (k => r[k] = (r[k] || 0) + 1)(e.join('-')));
        return r;
    }, {}),
    result = map1.reduce((r, { id, res }) => {
        r[id] ??= {};
        Object.entries(res).forEach(([k, v]) => r[id][k] = temp[v] || 0);
        return r;
    }, {});

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

您可以结合使用 reducemapfilter 方法。

const map1 = [{"id":1,"res":{"a":"1-a","b":"1-b"}},{"id":2,"res":{"a":"2-a","b":"2-b","c":"2-c","d":"2-d"}},{"id":3,"res":{"a":"3-a","b":"3-b","c":"3-c"}}]
const map2 = [{"map1":{"1":"a","2":"c","3":"b"}},{"map1":{"1":"b","2":"c","3":"a"}},{"map1":{"1":"a","2":"a"}},{"map1":{"1":"a","2":"a","3":"b"}},{"map1":{"2":"d","3":"a"}},{"map1":{"1":"a","2":"c","3":"c"}},{"map1":{"1":"b","2":"d","3":"c"}},{"map1":{"1":"b","3":"a"}}]

const result = map1.reduce((r, { id }) => {
  r[id] = map2
    .map(({ map1 }) => map1[id])
    .filter(Boolean)
    .reduce((a, e) => {
      if (!a[e]) a[e] = 0;
      a[e] += 1
      return a
    }, {})

  return r
}, {})

console.log(result)

您可以首先创建一个查找 table 以通过循环遍历 map2 来创建一个由 1-a2-b 等键控的对象来快速检索。其中每个值存储每个键的 count/occurrences。然后,一旦你有了查找 table,你就可以在你的 map1 上使用 .map() and Object.fromEntries() 来使用之前构建的查找 table.

构建结果对象

const map1 = [
  { id: 1, res: { a: `1-a`, b: `1-b` } },
  { id: 2, res: { a: `2-a`, b: `2-b`, c: `2-c`, d: `2-d` } },
  { id: 3, res: { a: `3-a`, b: `3-b`, c: `3-c` } }
]
const map2 = [
  { map1: { 1: 'a', 2: 'c', 3: 'b' } },
  { map1: { 1: 'b', 2: 'c', 3: 'a' } },
  { map1: { 1: 'a', 2: 'a' } },
  { map1: { 1: 'a', 2: 'a', 3: 'b' } },
  { map1: { 2: 'd',         3: 'a' } },
  { map1: { 1: 'a', 2: 'c', 3: 'c' } },
  { map1: { 1: 'b', 2: 'd', 3: 'c' } },
  { map1: { 1: 'b',         3: 'a' } }
];


const summed = map2.reduce((acc, {map1}) => {
  Object.entries(map1).forEach(([key, val]) => {
    acc[`${key}-${val}`] = (acc[`${key}-${val}`] || 0) +1;
  });
  return acc;
}, {});

const result = Object.fromEntries(map1.map(({id, res}) => [
  id,
  Object.fromEntries(Object.entries(res).map(([key, val]) => [key, summed[val] || 0]))
]));

console.log(result);

我是这样解决的:

const result = map1.reduce((counterAcc, m1) => {
  const { id, res } = m1
  const resIds = Object.keys(res)
  const res2Ids = map2.map(({ map1 }) => map1[id])
  const res2IdsWithoutUndefined = res2Ids.filter(Boolean)
  
  counterAcc[id] = resIds.reduce((accumulator, resId) => {    
    accumulator[resId] = 0
    const counterBy = res2IdsWithoutUndefined.reduce((resAcc, ansId) => {
      const answerText = { text: res[ansId] }
      if (!resAcc[ansId]) {
        resAcc[ansId] = 1
      } else {
        resAcc[ansId] += 1
      }
      return resAcc;
    }, {})
    
    return { ...accumulator, ...counterBy }
  }, {})
  return counterAcc
}, {})