0

The challenge description is as follows:

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

The input is as follows:

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

This is what the output should look like:

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

I got the sorting done. This code returns an array of objects which key has all common values, the object and sorted by date:

function findDuplicateTransactions (transactions = []) {
return transactions.map(t => ({
    key: JSON.stringify([t.sourceAccount, t.targetAccount, t.amount, t.category]), 

    t
})).sort((a,b) => 
    a.t.time.localeCompare(b.t.time)).sort((a, b) =>a.key < b.key ? -1 : 1)}

I´m missing the last step, in which I should compare one agaisnt each other and form arrays. I´m guessing reduce is in order, but I can´t wrap my head around it. Could anyone help?

3 Answers3

1

First map the array using the same values, then sort it on the basis of time like below:

var 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'
  }
];

function findDuplicateTransactions(transactions = []) {
  return transactions.reduce((ini, curr, idx) => {
    if (ini[
        curr.sourceAccount +
        curr.targetAccount +
        curr.amount +
        curr.category]) {
      ini[curr.sourceAccount + curr.targetAccount + curr.amount + curr.category]
        .push(curr);
    } else {
      ini[
        curr.sourceAccount +
        curr.targetAccount +
        curr.amount +
        curr.category] = [curr];
    }
    return ini;
  }, []);

}

var result = Object.values(findDuplicateTransactions(arr));

var expected = result.map(x => sortDate(x)).map(y => checkTimeDiff(y));

function checkTimeDiff(array) {
  var returnArr = [];
  returnArr.push(array[0]);
  for (var i = 0; i < array.length - 1; i++) {
    if ((new Date(array[i + 1].time) - new Date(array[i].time)) / 60000 <= 1) {
      returnArr.push(array[i + 1]);
    }
  }
  return returnArr;
}

function sortDate(array) {
  return array.sort(function(a, b) {
    var keyA = new Date(a.time),
      keyB = new Date(b.time);
    // Compare the 2 dates
    if (keyA < keyB) return -1;
    if (keyA > keyB) return 1;
    return 0;
  });
}
console.log(expected);
Sunil Lama
  • 4,531
  • 1
  • 18
  • 46
0

Consider that Transaction is an object which can be taught to say whether it's a dup of another transaction...

const transactionData = [{
    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'
  }
]


class Transaction {
  constructor(obj) {
    Object.assign(this, obj)
  }
  isDupOf(xaction) {
    return (
      this.id !== xaction.id &&  // transactions are not dups of themselves
      this.sourceAccount == xaction.sourceAccount &&
      this.targetAccount == xaction.targetAccount &&
      this.category == xaction.category &&
      this.amount == xaction.amount &&
      this.hasAboutTheSameTimeAs(xaction))

  }
  hasAboutTheSameTimeAs(xaction) {
    let msDiff = Math.abs(new Date(this.time) - new Date(xaction.time))
    return msDiff < 60000
  }
}

// transform the data into objects
const transactions = transactionData.map(d => new Transaction(d))

// this could be a static method on transaction
function isDupWithinTransactions(xaction) {
  return transactions.some(t => t.isDupOf(xaction))
}

const dups = transactions.filter(t => isDupWithinTransactions(t))
console.log(dups.sort((a,b) => a.id - b.id))
danh
  • 62,181
  • 10
  • 95
  • 136
0

Didn't do any fancy map, reduce, filter, etc. just a little brute force iteration but I think this gives you what you want. Basically took the result of your findDuplicateTransactions (note I added a new attribute to you map in the function called dt that contains milliseconds for the timestamp for coparison purposes) and then did the following:

  1. filter to remove any transactions that are not within 1 minute of the transaction before/after with the same key
  2. loop through putting each unique key in its own sub array of a master array.

masterArray should contain what you want.

function findDuplicateTransactions (transactions = []) {
    return transactions.map(t => ({
    key: JSON.stringify([t.sourceAccount, t.targetAccount, t.amount, t.category]), 
    dt: new Date(t.time).valueOf(),
    t
    })).sort((a,b) => 
    a.t.time.localeCompare(b.t.time)).sort((a, b) =>a.key < b.key ? -1 : 1)
}
var sortedArray = findDuplicateTransactions(transactionData);
var filteredArray = [];
for (var i=0; i<sortedArray.length; i++) {
    if ((i<sortedArray.length-1 && sortedArray[i].key == sortedArray[i+1].key && sortedArray[i+1].dt - sortedArray[i].dt < 60000) || 
        (i>0 && sortedArray[i].key == sortedArray[i-1].key && sortedArray[i].dt - sortedArray[i-1].dt < 60000)) 
      filteredArray.push(sortedArray[i]);
}
var masterArray = [];
var subArray = [];
var lastkey = "";
for (var i=0; i<filteredArray.length; i++) {
    if (filteredArray[i].key != lastkey) {
        if (subArray.length>0) masterArray.push(subArray);
        subArray = [];
    }
    subArray.push(filteredArray[i]);
    lastkey = filteredArray[i].key;
}
masterArray.push(subArray);

Chris H
  • 501
  • 1
  • 4
  • 15