1

I have an array containing a customer ID, value of transaction, and transaction ID for each transaction performed by the customer.

I have 20,000 transactions performed by 9,000 customers. I want one customer ID, an array of all the prices per that customer ID, and an array of all the transaction Ids per customer ID.

Currently looks like this:

var transactionArray =
{
  customerId: '1',
  price: [ 100 ],
  transactionID: ['00a13']
},
{
  customerId: '2',
  price: [ 200 ],
  transactionID: ['00a14']
},
{
  customerId: '1',
  price: [ 700 ],
  transactionID: ['00a15']
},
{
  customerId: '2',
  price: [ 1700 ],
  transactionID: ['00a16']
},

... 19996 more items

and I'd like it to look like this:

var customerArray =

{
  customerId: '1',
  price: [ 100, 700 ],
  transactionID: ['00a13', '00a15']
},
{
  customerId: '2',
  price: [ 200, 1700 ],
  transactionID: ['00a14', '00a16']
},

...8998 more items
4
  • In your initial data, is the price and transaction data stored as individual arrays only containing 1 item? Commented Jun 1, 2020 at 19:15
  • Correct. It wasn't at first, but I tried to use a for loop to concat the arrays inside each object. I'm intentionally pushing it to the transaction array as an array, but it could be switched back. Commented Jun 1, 2020 at 19:20
  • Have look at groupBy lodash.com/docs/4.17.15#groupBy function by lodash if you give your array as the first parameter and customerId as the second, it would group items by their ids, then you can flatten the grouped data or use a reducer. Commented Jun 1, 2020 at 19:23
  • use an object or map to collect all the unique ids, then just concat each price and transaction together. Commented Jun 1, 2020 at 19:24

2 Answers 2

1

Just using reduce and push the elements onto the array

var transactionArray = [{
    customerId: '1',
    price: [100],
    transactionID: ['00a13']
  },
  {
    customerId: '2',
    price: [200],
    transactionID: ['00a14']
  },
  {
    customerId: '1',
    price: [700],
    transactionID: ['00a15']
  },
  {
    customerId: '2',
    price: [1700],
    transactionID: ['00a16']
  },
]

var results = Object.values(transactionArray.reduce((custs, { customerId, price, transactionID }) => {
  var customer = custs[customerId]
  if (!customer) {
    custs[customerId] = {
      customerId: customerId,
      price: [...price],
      transactionID: [...transactionID]
    }
  } else {
    customer.price = [...customer.price, ...price]
    customer.transactionID =  [...customer.transactionID, ...transactionID]
  }

  return custs
}, {}))

console.log(results)

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

10 Comments

I think this answer assumes that customerId will always be an integer string. If the customerId has anything other than positive integers, getting the customer out of the custs won't work, right?
@SinanBolel why would it matter what it is? It is a string.
If customer id's looked like aef64d, would var customer = custs[customerId] still find the customer?
I maybe misunderstanding something here. This is logging. { customerId: '1', price: [100], transactionId: ['00a13'], undefined:{ price: [ an array made up of 19,999 other prices], transactionId: [an array made up of 19,999 other transactionIds]
Even if there was... var arr = []; arr[-1] = 11111; console.log(arr);
|
0

It's convenient that customerId's are integers. If that ever changes, then you will need to index them, then rebuild the object.

// create a separate array for holding order of customerIds
const customerIds = []

const result = transactionArray
  .reduce((acc, { customerId, price, transactionID }) => {
    const idIndex = customerIds.indexOf(customerId)
    // check if customerId exists in customerIds array
    if (idIndex < 0) {
      // if it doesn't, add it to the customerId's array
      customerIds.push(customerId)
      // then add the matching price and transactionID to the accumulator
      // of this reduce, spreading the contents of the array into 2 new arrays.
      acc.push([[...price], [...transactionID]])
    } else {
      // if the customerId is already accounted for, then
      // add the price and transactionID to that customer's bucket
      // at the same index where the customerId exists inside customerIds
      acc[idIndex][0].push(...price)
      acc[idIndex][1].push(...transactionID)
    }
    return acc
  }, [])
  // finally, convert the arrays back into objects
  .map((value, index) => {
    return ({ 
      customerId: customerIds[index],
      price: value[0],
      transactionID: value[1],
    })
  })


console.log(result)

which logs:

[
  {
    customerId: '1',
    price: [ 100, 700 ],
    transactionID: [ '00a13', '00a15' ]
  },
  {
    customerId: '2',
    price: [ 200, 1700 ],
    transactionID: [ '00a14', '00a16' ]
  }
]

If the customerIds were strings that didn't represent integers, this will still work -- for example, if your customer data looked like this:

const transactionArray = [
  {
    customerId: '324asdrea',
    price: [ 100 ],
    transactionID: ['00a13']
  },
  {
    customerId: '4hdffgi2',
    price: [ 200 ],
    transactionID: ['00a14']
  },
  {
    customerId: '324asdrea',
    price: [ 700 ],
    transactionID: ['00a15']
  },
  {
    customerId: '4hdffgi2',
    price: [ 1700 ],
    transactionID: ['00a16']
  }
]

which results in:

[
  {
    customerId: '324asdrea',
    price: [ 100, 700 ],
    transactionID: [ '00a13', '00a15' ]
  },
  {
    customerId: '4hdffgi2',
    price: [ 200, 1700 ],
    transactionID: [ '00a14', '00a16' ]
  }
]

4 Comments

Note that @epascarello works as well and only has one reduce, and is the better answer.
A true hero! Thank you! I've been beating my head against the wall for days trying to do this in a concise way. This helps a ton. I'm simultaneously super bummed out, because I obviously don't understand reducer functions. lol
Actually scratch that, I just ran tests on jsperf and this answer outperforms the other, marginally.
@FreshCeviche you're welcome, glad you got it. Functional javascript is great :)

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.