BreadBoard.ini
BreadBoard.ini

Reputation: 143

How to merge two object arrays of differing length, based on two object key

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

Upvotes: 0

Views: 522

Answers (5)

Nick
Nick

Reputation: 147146

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 }

Upvotes: 2

malarres
malarres

Reputation: 2946

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

Upvotes: 1

Xupitan
Xupitan

Reputation: 1681

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)

Upvotes: 1

Faizanur Rahman
Faizanur Rahman

Reputation: 534

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


Upvotes: 1

jsN00b
jsN00b

Reputation: 3691

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.

Upvotes: 1

Related Questions