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



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

以下是交易的输入。

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

var newArray = transactions.reduce(function(acc, curr) {
//finding Index in the array where the NamaCategory matched
var findIfduplicateExist = acc.findIndex(function(item) {
let accepoch = new Date(item.time).valueOf();
let currepoch= new Date(curr.time).valueof();
if(item.sourceAccount === curr.sourceAccount &&
item.targetAccount===curr.targetAccount &&
item.amount===curr.amount&&
accepoch<currepoch+(1*60*1000))
let obj={
'id':curr.id,
'sourceAccount':curr.sourceAccount,
'targetAccount':curr.targetAccount,
'time':curr.time,
'category':curr.category,
'amount':curr.amount 
}
})
// if in the new array no such object exist, create a new object 
if (findIfNameExist === -1) {
acc.push(obj)
} else {
// if attributes matches , then push the value 
acc[findIfNameExist].value.push(curr)
}
return acc;
}, []);

输入交易:

[
{
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"
}   
] 
]

当您第一次获得按id排序的事务的副本时,会更容易(也更高效(。我假设id是一个递增的数字,这样以后的事务总是有更大的数字。这样,您只需将时间戳与累加器中的最后一个时间戳进行比较:

// Example data
const transactions = [ { 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' } ];
const newArray = [...transactions].sort((a,b) => a.id - b.id).reduce( (acc, curr) => {
let group = acc[acc.length-1], 
prev = group && group[group.length-1];
if (!prev || prev.sourceAccount !== curr.sourceAccount ||
prev.targetAccount !== curr.targetAccount ||
prev.amount !== curr.amount ||
Date.parse(prev.time) + (1*60*1000) < Date.parse(curr.time)) {
// different keys or larger time difference: create new group
acc.push(group = []);
}
group.push(curr);
return acc;
}, []);
console.log(newArray);

这可以通过一个Array.sort、Array.reduce和Object.values:以简洁的方式完成

const data = [{ 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' }] 
const sort = arr => arr.sort((a,b) =>`${a.id}${a.time}`.localeCompare(`${b.id}${b.time}`))
const getTime = obj => new Date(obj.time).getTime()
const isDub = (arr, obj) => arr.length ? Math.abs(getTime(arr[arr.length-1]) - getTime(obj))/1000 > 60 : false
const result = Object.values(sort(data).reduce((r, c) => {
let key = [c.sourceAccount, c.targetAccount].join('-')
r[key] = isDub(r[key] || [], c) ? r[key] : [...r[key] || [], c]
return r
}, {}))
console.log(result)

您确实需要对数组进行预排序,以便在根据您的分钟内需求进行重复比较时只处理最后一个条目。

您可以进行多列排序,然后在每个组中找到重复项。

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

您也可以像下面这样使用Array.sortArray.forEach来实现此

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

let arr = [{  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'}];
let res = []
,  getKey = ({id, time, ...rest}) => Object.entries(rest).map(([k, v]) => k + '-' + v).join(';')
,  getTimeDiff = (t1, t2) => Math.abs(new Date(t1).getTime() - new Date(t2).getTime())
arr.sort((a,b) => {
let akey = getKey(a)
, bkey = getKey(b)

return akey.localeCompare(bkey) || +new Date(a.time) - +new Date(b.time)
})
.forEach((d, i, t) => 
i == 0 || 
(getKey(d) == getKey(t[i-1]) && getTimeDiff(t[i-1].time, d.time)/1000 < 60)
? res.push((res.pop() || []).concat(d))
: res.push([d])
)
console.log(res)

最新更新