使用JavaScript在事务对象数组中查找重复项,并在数组对象数组中查找组合重复项

使用JavaScript在事务对象数组中查找重复项,并在数组对象数组中查找组合重复项,javascript,arrays,duplicates,javascript-objects,Javascript,Arrays,Duplicates,Javascript Objects,我有一个事务对象数组,需要根据属性查找重复的对象(如果对象的所有值都相同(ID和时间除外),则该对象是重复的,时间差应在1分钟内)。 我需要将相同的重复事务合并为一个数组对象 以下是交易的输入 我尝试使用Reduce函数,但无法获得预期的输出 预期产出如下: [ [ { id: 1, sourceAccount: "A", targetAccount: "B", amount: 100, category: "eati

我有一个事务对象数组,需要根据属性查找重复的对象(如果对象的所有值都相同(ID和时间除外),则该对象是重复的,时间差应在1分钟内)。 我需要将相同的重复事务合并为一个数组对象

以下是交易的输入

我尝试使用Reduce函数,但无法获得预期的输出

预期产出如下:

[   
  [
    {
      id: 1,
      sourceAccount: "A",
      targetAccount: "B",
      amount: 100,
      category: "eating_out",
      time: "2018-03-02T10:33:00.000Z"
    },
    {
      id: 2,
      sourceAccount: "A",
      targetAccount: "B",
      amount: 100,
      category: "eating_out",
      time: "2018-03-02T10:33:50.000Z"
    },
    {
      id: 3,
      sourceAccount: "A",
      targetAccount: "B",
      amount: 100,
      category: "eating_out",
      time: "2018-03-02T10:34:30.000Z"
    }  
  ], 
  [
    {
      id: 5,
      sourceAccount: "A",
      targetAccount: "C",
      amount: 250,
      category: "other",
      time: "2018-03-02T10:33:00.000Z"
    },
    {
      id: 6,
      sourceAccount: "A",
      targetAccount: "C",
      amount: 250,
      category: "other",
      time: "2018-03-02T10:33:05.000Z"
    }   
  ] 
]
当您第一次获得按id排序的事务副本时,会更容易(也更高效)。我假设id是一个递增的数字,因此以后的事务总是有一个更大的数字。这样,您只需将时间戳与累加器中的最后一个时间戳进行比较:

//示例数据
const transactions=[{id:3,sourceAccount:'A',targetAccount:'B',金额:100,类别:'eating_out',时间:'2018-03-02T10:34:30.000Z'},{id:1,sourceAccount:'A',targetAccount:'B',金额:100,类别:'eating_out',时间:'2018-03-02T10:33:00.000Z'},{id:6,源账户:'A',目标账户:'C',金额:250,类别:'other',时间:'2018-03-02T10:33:05.000Z'},{id:4,源账户:'A',目标账户:'B',金额:100,类别:'eating_out',时间:'2018-03-02T10:36:00.000Z'},{id:2,源账户:'A',目标账户:'B',金额:100,类别:'eating_out',时间:'2018-03-02T10:33:50.000Z'},{id:5,源账户:'A',目标账户:'C',金额:250,类别:'other',时间:'2018-03-02T10:33:00.000Z'};
const newArray=[…事务].sort((a,b)=>a.id-b.id).reduce((acc,curr)=>{
让组=附件[附件长度-1],
prev=组和组[group.length-1];
如果(!prev | | prev.sourceAccount!==curr.sourceAccount||
上一个目标帐户!==当前目标帐户||
上一笔金额!==当前金额||
Date.parse(上一个时间)+(1*60*1000)
当您第一次获得按id排序的事务副本时,它将更容易(更高效)。我假设id是一个递增的数字,以便以后的事务总是有一个更大的数字。这样,您只需将时间戳与累加器中的最后一个进行比较:

//示例数据
const transactions=[{id:3,sourceAccount:'A',targetAccount:'B',金额:100,类别:'eating_out',时间:'2018-03-02T10:34:30.000Z'},{id:1,sourceAccount:'A',targetAccount:'B',金额:100,类别:'eating_out',时间:'2018-03-02T10:33:00.000Z'},{id:6,源账户:'A',目标账户:'C',金额:250,类别:'other',时间:'2018-03-02T10:33:05.000Z'},{id:4,源账户:'A',目标账户:'B',金额:100,类别:'eating_out',时间:'2018-03-02T10:36:00.000Z'},{id:2,源账户:'A',目标账户:'B',金额:100,类别:'eating_out',时间:'2018-03-02T10:33:50.000Z'},{id:5,源账户:'A',目标账户:'C',金额:250,类别:'other',时间:'2018-03-02T10:33:00.000Z'};
const newArray=[…事务].sort((a,b)=>a.id-b.id).reduce((acc,curr)=>{
让组=附件[附件长度-1],
prev=组和组[group.length-1];
如果(!prev | | prev.sourceAccount!==curr.sourceAccount||
上一个目标帐户!==当前目标帐户||
上一笔金额!==当前金额||
Date.parse(上一个时间)+(1*60*1000)console.log(newArray);
您也可以使用
Array.sort
Array.forEach
来实现这一点,如下所示

我最初通过连接属性值(不包括
id
time
)和增加时间戳对数组进行排序

let arr=[{id:3,sourceAccount:'A',targetAccount:'B',金额:100,类别:'eating_out',时间:'2018-03-02T10:34:30.000Z'},{id:1,sourceAccount:'A',targetAccount:'B',金额:100,类别:'eating_out',时间:'2018-03-02T10:33:00.000Z'},{id:6,源账户:'A',目标账户:'C',金额:250,类别:'other',时间:'2018-03-02T10:33:05.000Z'},{id:4,源账户:'A',目标账户:'B',金额:100,类别:'eating_out',时间:'2018-03-02T10:36:00.000Z'},{id:2,源账户:'A',目标账户:'B',金额:100,类别:'eating_out',时间:'2018-03-02T10:33:50.000Z'},{id:5,源账户:'A',目标账户:'C',金额:250,类别:'other',时间:'2018-03-02T10:33:00.000Z'};
让res=[]
,getKey=({id,time,…rest})=>Object.entries(rest.map)([k,v])=>k+'-'+v.join(“;”)
,getTimeDiff=(t1,t2)=>Math.abs(新日期(t1).getTime()-新日期(t2).getTime())
arr.sort((a,b)=>{
设akey=getKey(a)
,bkey=getKey(b)
返回akey.localeCompare(bkey)| |+新日期(a.time)-+新日期(b.time)
})
.forEach((d,i,t)=>
i==0 | |
(getKey(d)==getKey(t[i-1])&&getTimeDiff(t[i-1]。时间,d.time)/1000<60)
?res.push((res.pop()| |[]).concat(d))
:res.push([d])
)

console.log(res)
您也可以使用
Array.sort
Array.forEach
来实现这一点

我最初通过连接属性值(不包括
id
time
)和增加时间戳对数组进行排序

let arr=[{id:3,sourceAccount:'A',targetAccount:'B',金额:100,类别:'eating_out',时间:'2018-03-02T10:34:30.000Z',{id:1,sourceAccount:'A',targetAccount:'B',金额:100,类别:'eating'
[
  {
    id: 3,
    sourceAccount: 'A',
    targetAccount: 'B',
    amount: 100,
    category: 'eating_out',
    time: '2018-03-02T10:34:30.000Z'
  },
  {
    id: 1,
    sourceAccount: 'A',
    targetAccount: 'B',
    amount: 100,
    category: 'eating_out',
    time: '2018-03-02T10:33:00.000Z'
  },
  {
    id: 6,
    sourceAccount: 'A',
    targetAccount: 'C',
    amount: 250,
    category: 'other',
    time: '2018-03-02T10:33:05.000Z'
  },
  {
    id: 4,
    sourceAccount: 'A',
    targetAccount: 'B',
    amount: 100,
    category: 'eating_out',
    time: '2018-03-02T10:36:00.000Z'
  },
  {
    id: 2,
    sourceAccount: 'A',
    targetAccount: 'B',
    amount: 100,
    category: 'eating_out',
    time: '2018-03-02T10:33:50.000Z'
  },
  {
    id: 5,
    sourceAccount: 'A',
    targetAccount: 'C',
    amount: 250,
    category: 'other',
    time: '2018-03-02T10:33:00.000Z'
  }
];
[   
  [
    {
      id: 1,
      sourceAccount: "A",
      targetAccount: "B",
      amount: 100,
      category: "eating_out",
      time: "2018-03-02T10:33:00.000Z"
    },
    {
      id: 2,
      sourceAccount: "A",
      targetAccount: "B",
      amount: 100,
      category: "eating_out",
      time: "2018-03-02T10:33:50.000Z"
    },
    {
      id: 3,
      sourceAccount: "A",
      targetAccount: "B",
      amount: 100,
      category: "eating_out",
      time: "2018-03-02T10:34:30.000Z"
    }  
  ], 
  [
    {
      id: 5,
      sourceAccount: "A",
      targetAccount: "C",
      amount: 250,
      category: "other",
      time: "2018-03-02T10:33:00.000Z"
    },
    {
      id: 6,
      sourceAccount: "A",
      targetAccount: "C",
      amount: 250,
      category: "other",
      time: "2018-03-02T10:33:05.000Z"
    }   
  ] 
]
const SECONDS = 60;
const MILLISECONDS = 1000;

const getTimeDifference = (t1, t2) => {
  return new Date(t1) - new Date(t2);
};

const multiLevelSort = (transactions = [], colsToSort = []) => {
  return transactions.sort((a, b) => {
    return colsToSort.reduce((acc, col) => {
      if (acc !== 0 || a[col] == b[col]) {
        return acc;
      }

      const c1 = a[col], c2 = b[col];
      if (col === "time") {
        return getTimeDifference(c1, c2) > 0 ? 1 : -1;
      } else {
        return c1 > c2 ? 1 : -1;
      }
    }, 0);
  });
};

const isUniqueTransaction = (prev, curr, matchKeys = []) => {
  if (!prev || !curr) {
    return true;
  }
  return matchKeys.reduce((acc, key) => {
    /* Current key is time then difference should be more than equal
     * 1 min for transaction to be unique.
     */
    if (key === "time") {
      return (
        acc ||
        getTimeDifference(curr[key], prev[key]) >= 1 * SECONDS * MILLISECONDS
      );
    }

    return acc || prev[key] !== curr[key];
  }, false);
};

function findDuplicateTransactions(transactions = []) {
  const matchingKeys = [
    "sourceAccount",
    "targetAccount",
    "amount",
    "category",
    "time"
  ];
  const sortedTransactions = multiLevelSort(transactions, matchingKeys);
  let duplicates = [];
  let group = [];
  sortedTransactions.forEach((curr, idx, transactions) => {
    // Previous Transaction find check if current trasaction is unique.
    const prev = group && group[group.length - 1];
    const isUnique = isUniqueTransaction(prev, curr, matchingKeys);
    if (isUnique) {
      if (group.length > 1) {
        duplicates.push(group);
      }
      group = [];
    }
    group.push(curr);
  });

  // Push last group if it has more than 1 transaction
  if (group.length > 1) {
    duplicates.push(group);
  }

  // Sort duplicate trasaction groups based on first transaction in group
  return duplicates.sort((a, b) => {
    return getTimeDifference(a[0].time, b[0].time);
  });
}