Categorize similar transactions

Given a list of transactions, some transactions are categorized, some transactions are not categorized. Find similar transactions and categorize them if possible. Similar transactions have the same targetAccount and the amount difference is not greater than 1000 (for all currencies) from the originally categorized transaction. If an uncategorized transaction is similar to more than one transaction, it should take the category from the one with the smallest amount difference. Transactions that cannot be categorized should still be included in the returned list. The returned list should preserve the order of the original list.

categorizeSimilarTransactions(transactions)

Input You can assume that the transactions parameter will always be present and valid. list of transactions (Transaction[])

Output List of transactions(Transaction[]) with enhanced categorization if possible.

This is what a categorized transaction looks like:

    {
      id: "bfd6a11a-2099-4b69-a7bb-572d8436cf73",
      sourceAccount: "my_account",
      targetAccount: "coffee_shop",
      amount: -350,
      currency: "EUR",
      category: "eating_out",
      time: "2021-03-12T12:34:00Z"
    }

An uncategorized transaction does not have category property. This is what an uncategorized transaction looks like:

    {
      id: "0f0ffbf9-2e26-4f5a-a6c0-fcbd504002f8",
      sourceAccount: "my_account",
      targetAccount: "eating_out",
      amount: -1900,
      time: "2021-03-12T12:34:00Z"
    }

The following two transactions are similar:

    {
      id: "bfd6a11a-2099-4b69-a7bb-572d8436cf73",
      sourceAccount: "my_account",
      targetAccount: "coffee_shop",
      amount: -350,
      category: "eating_out",
      time: "2021-03-12T12:34:00Z"
    }

and

    {
      id: "a001bb66-6f4c-48bf-8ae0-f73453aa8dd5",
      sourceAccount: "my_account",
      targetAccount: "coffee_shop",
      amount: -620,
      time: "2021-04-10T10:30:00Z"
    }

Input

    categorizeSimilarTransactions([
      {
        id: "a001bb66-6f4c-48bf-8ae0-f73453aa8dd5",
        sourceAccount: "my_account",
        targetAccount: "coffee_shop",
        amount: 350,
        time: "2021-04-10T10:30:00Z",
      },
      {
        id: "bfd6a11a-2099-4b69-a7bb-572d8436cf73",
        sourceAccount: "my_account",
        targetAccount: "coffee_shop",
        amount: -150,
        category: "eating_out",
        time: "2021-03-12T12:34:00Z",
      },
      {
        id: "6359091e-1187-471f-a2aa-81bd2647210f",
        sourceAccount: "my_account",
        targetAccount: "coffee_shop",
        amount: 100,
        category: "entertainment",
        time: "2021-01-12T08:23:00Z",
      },
      {
        id: "a8170ced-1c5f-432c-bb7d-867589a9d4b8",
        sourceAccount: "my_account",
        targetAccount: "coffee_shop",
        amount: -1690,
        time: "2021-04-12T08:20:00Z",
      },
    ]);

Expected Output

    [
      {
        id: "a001bb66-6f4c-48bf-8ae0-f73453aa8dd5",
        sourceAccount: "my_account",
        targetAccount: "coffee_shop",
        amount: 350,
        category: "entertainment",
        time: "2021-04-10T10:30:00Z",
      },
      {
        id: "bfd6a11a-2099-4b69-a7bb-572d8436cf73",
        sourceAccount: "my_account",
        targetAccount: "coffee_shop",
        amount: -150,
        category: "eating_out",
        time: "2021-03-12T12:34:00Z",
      },
      {
        id: "6359091e-1187-471f-a2aa-81bd2647210f",
        sourceAccount: "my_account",
        targetAccount: "coffee_shop",
        amount: 100,
        category: "entertainment",
        time: "2021-01-12T08:23:00Z",
      },
      {
        id: "a8170ced-1c5f-432c-bb7d-867589a9d4b8",
        sourceAccount: "my_account",
        targetAccount: "coffee_shop",
        amount: -1690,
        time: "2021-04-12T08:20:00Z",
      },
    ];

I could find a solution but it's a brute-force approach & not efficient. Is there any other solution with less time complexity may be O(n) or O(log(n))/O(nlog(n)) which is easy to read & understand

      const categorizeSimilarTransactions = (transactions) => {
        if (!(Array.isArray(transactions) && transactions.length)) return [];
          const categorizedTransactions = transactions.filter(t => t.category);
          const uncategorizedTransactions = transactions.filter(t => !t.category);

          let lowestDiff;
          uncategorizedTransactions.forEach(uncategorizedTransaction => {
              lowestDiff = Math.min();
              categorizedTransactions.forEach(cat => {
                  if (cat.targetAccount === uncategorizedTransaction.targetAccount) {
                      lowestDiffComp = Math.abs(cat.amount - uncategorizedTransaction.amount);

                      if (lowestDiffComp < lowestDiff && lowestDiffComp < 1000) {
                          lowestDiff = lowestDiffComp;
                          uncategorizedTransaction.category = cat.category;
                      }
                  }
              })
          });
          const merged = [...categorizedTransactions, ...uncategorizedTransactions];
          const orderIds = transactions.map(transaction => transaction.id);
          merged.sort((a, b) => {
              return orderIds.indexOf(a.id) - orderIds.indexOf(b.id);
          })
          return merged;
      };
How many English words
do you know?
Test your English vocabulary size, and measure
how many words do you know
Online Test
Powered by Examplum