0

I have 2 arrays, I'd like to combine them if they have the same two object keys.

If no match is found, still keep the object, but have the value as 0.

Example of Input

withdrawal: [
    {
        "id": "a1",
        "withdrawalAmount": 300,
        "user": "John"
    },
    {
        "id": "b2",
        "withdrawalAmount": 100,
        "user": "Mike"
    }
    {
        "id": "c3",
        "withdrawalAmount": 33,
        "user": "John"
    }
]


deposit: [
    {
        "id": "a1",
        "depositAmount": 123,
        "user": "John"
    },
    {
        "id": "c3",
        "depositAmount": 44,
        "user": "John"
    },
]

Expected Output

transactions: [
    {
        "id": "a1",
        "depositAmount": 123,
        "withdrawalAmount": 300,
        "user": "John"
    },
    {
        "id": "b2",
        "depositAmount": 0,
        "withdrawalAmount": 100,
        "user": "Mike"
    },
    {
        "id": "c3",
        "depositAmount": 44,
        "withdrawalAmount": 33
        "user": "John"
    },
]

This is the function I tried so far, but it doesn't work due the two input arrays being a different length.

function mergeArrayObjects(arr1, arr2) {
    return arr1.map((item, i) => {
      if (item.user === arr2[i].user) {
        //merging two objects
        return Object.assign({}, item, arr2[i])
      }
    })
  }
4
  • The approach used will ignore elements in arr2 if those do not have a matching id in arr1. So, if the arr2 array had an element like: {"id" : "c3", "depositAmount": 333, "user": "tom"} and arr1 did not have a corresponding c3 element, then this deposit will be excluded from transactions. May be this is what is needed? Please update the question to clarify. Commented May 25, 2022 at 4:56
  • @jsN00b Thanks for pointing it out, updated the question, I would like it to not ignore elements if it does not have a matching id, but have the value of the amount set to 0, like the expected output above. Commented May 25, 2022 at 5:02
  • What @jsN00b was saying is what happens if there is that entry in deposit for id = 'c3', but there is no entry in withdrawal for id = 'c3'? Should there be an entry in transactions with withdrawalAmount = 0? Commented May 25, 2022 at 5:28
  • @Nick yes, correct Commented May 25, 2022 at 5:35

5 Answers 5

2

You can achieve the result you want by processing the list of withdrawals and deposits using Array.reduce to an object with the id values as keys and deposit and withdrawal amounts as values; you can then take the values of that object to make the transactions array:

const withdrawal = [{
    "id": "a1",
    "withdrawalAmount": 300,
    "user": "John"
  },
  {
    "id": "b2",
    "withdrawalAmount": 100,
    "user": "Mike"
  }
]

const deposit = [{
    "id": "a1",
    "depositAmount": 123,
    "user": "John"
  },
  {
    "id": "c3",
    "depositAmount": 44,
    "user": "John"
  }
]

const transactions = Object.values(
  withdrawal
    .concat(deposit)
    .reduce((c, { id, depositAmount, withdrawalAmount, ...rest }) => {
      c[id] = c[id] || {}
      depositAmount = c[id]['depositAmount'] || depositAmount || 0;
      withdrawalAmount = c[id]['withdrawalAmount'] || withdrawalAmount || 0;
      c[id] = ({ id, depositAmount, withdrawalAmount, ...rest });
      return c;
    },
    {})
  )
  
console.log(transactions)
.as-console-wrapper { max-height: 100% !important; top: 0 }

If you want to group by both id and user, you need to make the result object keys out of both values, joined by a character that is not in either of them (e.g. # would work for your data):

const withdrawal = [{
    "id": "a1",
    "withdrawalAmount": 300,
    "user": "John"
  },
  {
    "id": "b2",
    "withdrawalAmount": 100,
    "user": "Mike"
  }
]

const deposit = [{
    "id": "a2",
    "depositAmount": 123,
    "user": "John"
  },
  {
    "id": "b2",
    "depositAmount": 109,
    "user": "Mike"
  },
  {
    "id": "c3",
    "depositAmount": 44,
    "user": "John"
  }
]

const transactions = Object.values(
  withdrawal
    .concat(deposit)
    .reduce((c, { id, user, depositAmount, withdrawalAmount, ...rest }) => {
      key = `${id}#${user}`
      c[key] = c[key] || {}
      depositAmount = c[key]['depositAmount'] || depositAmount || 0;
      withdrawalAmount = c[key]['withdrawalAmount'] || withdrawalAmount || 0;
      c[key] = ({ id, user, depositAmount, withdrawalAmount, ...rest });
      return c;
    },
    {})
  )
  
console.log(transactions)
.as-console-wrapper { max-height: 100% !important; top: 0 }

Sign up to request clarification or add additional context in comments.

7 Comments

What happens if deposit had another element like so: {"id" : "c3", "depositAmount": 333, "user": "Nicholas"} ? Does it get reflected in transactions? Guess OP was not clear whether such an entry needs to be included or discarded. :-)
@jsN00b a good question - sadly OP has failed to confirm the answer with their edit...
@jsN00b Yes, it will get reflected in transactions, and if for that particular id and user, his "withdrawalAmount" would be 0, since he only made deposits.
@BreadBoard.ini I've updated my answer to deal with that situation
@Nick Thanks for the answer, so far it is working for me, but what if I would like to join it based on user AND id, instead of only id? Apologies if I reply too late
|
1

Presented below is one possible way to achieve the desired objective.

Code Snippet

// helper method to obtain all props 
// from array-elt matching "id"
const getInfo = (ar, argId) => (
  ar?.find(({ id }) => id === argId) ?? {}
);

// combine both arrays using "id"
const combineArrays = (ar1, ar2) => {
  // first get unique ids combining both arrays
  const uniqIds = [
    ...new Set(
      [...ar1, ...ar2]
      .map(({ id }) => id)
    )
  ];
  // now, for each unique id
  // simply get relevant info from both
  // arrays where element's match the "id"
  return uniqIds.map(id => ({
    id,
    ...getInfo(ar1, id),
    ...getInfo(ar2, id)
  }));
};

const withdrawal = [{
    "id": "a1",
    "withdrawalAmount": 300,
    "user": "John"
  },
  {
    "id": "b2",
    "withdrawalAmount": 100,
    "user": "Mike"
  }
];

const deposit = [{
  "id": "a1",
  "depositAmount": 123,
  "user": "John"
}];

// invoke the method and display the result
console.log(
  'combined both arrays as below:\n',
  combineArrays(withdrawal, deposit)
);
.as-console-wrapper { max-height: 100% !important; top: 0 }

Explanation

Inline comments added to the snippet above.

Comments

1

In first iteration merge only common object then re-iterate both array and take only un-used item in final array.

class Transaction {
  constructor(transaction) {
    this.id = transaction.id;
    this.depositAmount = transaction.depositAmount | 0;
    this.withdrawAmount = transaction.withdrawalAmount | 0;
    this.user = transaction.user;
  }
}

let withdrawl = [
  {
    id: "a1",
    withdrawalAmount: 300,
    user: "John",
  },
  {
    id: "b2",
    withdrawalAmount: 100,
    user: "Mike",
  },
  {
    id: "c3",
    withdrawalAmount: 33,
    user: "John",
  },
];

let deposit = [
  {
    id: "a1",
    depositAmount: 123,
    user: "John",
  },
  {
    id: "c3",
    depositAmount: 44,
    user: "John",
  },
];


let transactions = [];

// merge the common key of two array into one array
withdrawl.forEach((item1) => {
  deposit.forEach((item2) => {
    if (item1.id === item2.id) {
      let combined = { ...item1, ...item2 };
      let transaction = new Transaction(combined);
      transactions.push(transaction);

      // mark the item as used
      // or set some flag to know
      // the item is used in the array
      item1.used = true;
      item2.used = true;
    }
  })
})

// now take all the unused item from the both array
// and push them to the transactions array
withdrawl.forEach((item1) => {
  if (!item1.used) {
    let transaction = new Transaction(item1);
    transactions.push(transaction);
  }
});

deposit.forEach((item2) => {
  if (!item2.used) {
    let transaction = new Transaction(item2);
    transactions.push(transaction);
  }
});

console.log(transactions);


Comments

1

const withdrawal = [{
    "id": "a1",
    "withdrawalAmount": 300,
    "user": "John"
  },
  {
    "id": "b2",
    "withdrawalAmount": 100,
    "user": "Mike"
  }
]


const deposit = [{
  "id": "a1",
  "depositAmount": 123,
  "user": "John"
}, ]
const mergeArr = withdrawal.map(ele => {
  let other = deposit.find(({id}) => id === ele.id)
  return other ? {...ele, ...other} : {...ele, depositAmount : 0}
})
console.log(mergeArr)

Comments

1

For large arrays go with Nick's approach (you will save a lot of cycles in the end)

For small amount of items in the array, a solution with find() can be good enough:

const withdrawal= [
    {
        "id": "a1",
        "withdrawalAmount": 300,
        "user": "John"
    },
    {
        "id": "b2",
        "withdrawalAmount": 100,
        "user": "Mike"
    },
    {
        "id": "c3",
        "withdrawalAmount": 33,
        "user": "John"
    }
]


const deposit= [
    {
        "id": "a1",
        "depositAmount": 123,
        "user": "John"
    },
    {
        "id": "c3",
        "depositAmount": 44,
        "user": "John"
    }
]

const transactions = withdrawal.map(item => ({"depositAmount": 0, ...item}));

deposit.forEach( item => {
  const found = transactions.find(x => x.id === item.id);
  if (found) {
      Object.assign(found,item);
  } else {
    transactions.push(Object.assign(item,{"withdrawalAmount": 0}));
  }
  });


console.log(transactions)

NOTE: this code assumes that you want the union of both arrays rather than the intersection

3 Comments

Thx for the reply, only one thing is missing which is where the id is b2, i would like to display depositAmount as well, with 0 as the value. Since this user only made a withdrawal
then a default value is needed in both cases, i will edit my answer
@BreadBoard.ini now both _amount get a 0 value by default

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.