首页 > 解决方案 > 根据自定义逻辑减少对象数组

问题描述

我试图通过基于逻辑合并它们之间的键来减少对象数组。

解释

我想通过它们的键来减少以下数组(见下文):

  1. lines:合并它们之间共有的数字范围(例如:[1,2] + [3,2] = [1,2,3])
  2. dates: 合并与lines键关联的数组日期

应删除所有重复的数组和/或键。

例子 :

输入示例 A

const errors = [
  {
    "lines": [1, 2],
    "dates": [["2020-12-12","2020-12-19"], ["2020-12-13","2020-12-25"]]
  },
  {
    "lines": [1, 3],
    "dates": [["2020-12-12","2020-12-19"], ["2020-12-15","2020-12-17"]]
  },
  {
    "lines": [2, 3],
    "dates": [["2020-12-13","2020-12-25"], ["2020-12-15","2020-12-17"]]
  },
  {
    "lines": [3, 2],
    "dates": [["2020-12-15","2020-12-17"], ["2020-12-13","2020-12-25"]]
  }
]

输出示例 A

const expected = [{
  lines: [1, 2, 3],
  dates: [
    ["2020-12-12", "2020-12-19"],
    ["2020-12-13", "2020-12-25"],
    ["2020-12-05", "2020-12-20"],
    ["2020-12-15", "2020-12-17"]
  ]
}];

输入示例 B

const errors = [
  {
    lines: [1, 2],
    dates: [["2020-12-12", "2020-12-19"], ["2020-12-04", "2020-12-25"]]
  },
  {
    lines: [1, 5],
    dates: [["2020-12-12", "2020-12-19"], ["2020-12-05", "2020-12-20"]]
  },
  {
    lines: [2, 5],
    dates: [["2020-12-04", "2020-12-25"], ["2020-12-05", "2020-12-20"]]
  },
  {
    lines: [3, 4],
    dates: [["2020-10-19", "2020-10-25"], ["2020-10-24", "2020-10-27"]]
  },
  {
    lines: [4, 3],
    dates: [["2020-10-24", "2020-10-27"], ["2020-10-19", "2020-10-25"]]
  },
  {
    lines: [5, 2],
    dates: [["2020-12-05", "2020-12-20"], ["2020-12-04", "2020-12-25"]]
  }
];

输出示例 B

const expected = [
  {
    lines: [1, 2, 5],
    dates: [
      ["2020-12-12", "2020-12-19"],
      ["2020-12-04", "2020-12-25"],
      ["2020-12-05", "2020-12-20"]
    ]
  },
  {
    lines: [3, 4],
    dates: [
      ["2020-10-19", "2020-10-25"],
      ["2020-10-24", "2020-10-27"]
  ]
  }
];

我创建了一个沙箱来实现这一点: https ://codesandbox.io/s/lodash-sandbox-zsr9r

有三个例子,第三个不起作用。

我目前的实现:

const sanatizeErrors = errors.reduce((acc, currentError, i) => {
  const nextError = errors[i + 1]
  const hasOnlySingleError = errors.length === 1

  // The following const is not enough "strong" and it doesn't handle all cases
  const hasCommonErrorWithNextLine =
    nextError && _.includes(nextError.lines, currentError.lines[0])

  if (hasOnlySingleError) {
    return [{
      lines: currentError.lines,
      dates: currentError.dates
    }]
  }

  if (hasCommonErrorWithNextLine) {
    return [
      ...acc,
      {
        lines: _.uniq([
          ...currentError.lines,
          ...nextError.lines
        ]),
        dates: _.uniqWith(
          [
            ...currentError.dates,
            ...nextError.dates
        ], _.isEqual)
      }
    ]
  }

  return acc
}, [])

这个最终数组用于处理动态日期范围重叠。

任何亮点都值得赞赏=)

标签: javascriptlodashreduceoverlap

解决方案


一旦条件非常复杂并且数据结构也很复杂 - 代码非常混乱。将很高兴检查其他想法。我在片段中添加了注释来解释逻辑。如果您有任何问题 - 欢迎您

const errors = [
  {
    lines: [1, 2],
    dates: [["2020-12-12", "2020-12-19"], ["2020-12-04", "2020-12-25"]]
  },
  {
    lines: [1, 5],
    dates: [["2020-12-12", "2020-12-19"], ["2020-12-05", "2020-12-20"]]
  },
  {
    lines: [2, 5],
    dates: [["2020-12-04", "2020-12-25"], ["2020-12-05", "2020-12-20"]]
  },
  {
    lines: [3, 4],
    dates: [["2020-10-19", "2020-10-25"], ["2020-10-24", "2020-10-27"]]
  },
  {
    lines: [4, 3],
    dates: [["2020-10-24", "2020-10-27"], ["2020-10-19", "2020-10-25"]]
  },
  {
    lines: [5, 2],
    dates: [["2020-12-05", "2020-12-20"], ["2020-12-04", "2020-12-25"]]
  }
];

let output = errors.reduce((acc,rec) => {
  let i;
  //iterate through elements of accumulator to check where to merge
for (i = 0; i < acc.length; i++) {    
  let current = acc[i]
  // if there are duplicates
  if(current.lines.map(it => rec.lines.indexOf(it) > -1).some(it => it))
    {
      // combine and merge (remove duplicates) arrays of lines   
      acc[i].lines = (acc[i].lines.concat(rec.lines)).filter((it, i, arr) => arr.indexOf(it) === i)
      // combine and merge (remove duplicates) arrays of dates. In filter complex check to find lastPosition of elements. IndexOf wont't work when arrays are compared (as works for lines)
      acc[i].dates = (acc[i].dates.concat(rec.dates)).filter((it, i, arr) => {
  const lastPosition = arr.reduce((acc,rec, idx) => rec[0] === it[0] && rec[1] === it[1] ? idx: acc, 0)
  return i === lastPosition
})
    // if place for merge has been found - finish
    return acc
  }  
  }
    // if there is no place for merge  - add new group
   return [...acc, rec]
  
  },[])


    console.log('result', output);


推荐阅读