Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Filter Array of object based in duplicate keys

Sometimes when a customer is charged, there is a duplicate transaction created. We need to find those transactions so that they can be dealt with. Everything about the transaction should be identical, except the transaction id and the time at which it occurred, as there can be up to a minute delay.

i need to Find all transactions that have the same sourceAccount, targetAccount, category, amount, and the time difference between each consecutive transaction is less than 1 minute.

I'd been trying looping the array and using map to create a new array, but i don't know how to match the array without providing a reference for the value, due the values in the array are dynamical i can't know the value.

var transac = [
  {
    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'
  }
];

expected:

[
  [
    {
      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"
    }
  ]
];
like image 571
alexmorgan.cr Avatar asked Jan 20 '19 19:01

alexmorgan.cr


1 Answers

I would create a composed key for the key fields and translate the time to a number of milliseconds and then sort by those two elements. In a second step group those entries that have the same key and are at most 1 minute apart:

var transac = [{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 result = transac.map(t => ({
    key: JSON.stringify([t.sourceAccount, t.targetAccount, t.amount, t.category]), 
    epoch: Date.parse(t.time), 
    t
})).sort((a,b) => 
    a.key.localeCompare(b.key) || a.epoch - b.epoch || a.t.id - b.t.id
).reduce(([acc, prev], curr) => {
    if (!prev || curr.key != prev.key || curr.epoch - prev.epoch > 60000) acc.push([]);
    acc[acc.length-1].push(curr.t);
    return [acc, curr];
}, [[]])[0];

console.log(result);

Following up on a comment below, the above result includes all transactions. The ones that have "duplicates" (according to the definition in the question) are grouped together with their duplicates in sub arrays; those that do not have such duplicates appear alone in their own sub arrays.

So to only get the duplicates, add the appropriate filter:

const duplicates = result.filter(a => a.length > 1);
like image 145
trincot Avatar answered Oct 10 '22 12:10

trincot