User123123
User123123

Reputation: 573

Dynamically split an array into subarrays

const giftcards = [
  {
    fromuserid: 1,
    amount: 10,
    date: new Date(),
    touserid: 11,
  },
  {
    fromuserid: 1,
    amount: 20,
    date: new Date(),
    touserid: 11,

  },
  {
    fromuserid: 2,
    amount: 10,
    date: new Date(),
    touserid: 11,
  },
  {
    fromuserid: 2,
    amount: 10,
    date: new Date(),
    touserid: 11,

  },
  {
    fromuserid: 3,
    amount: 10,
    date: new Date(),
    touserid: 11,

  }
]

I achieved this, which is shown in the useEffect hook:

const giftcards = [
  {
    fromuserid: 1,
    amounts: [{
        amount: 10,
        date: new Date(),
        touserid: 11,
      },
      {
        amount: 20,
        date: new Date(),
        touserid: 11,
      }
    ]
  },

  {
    fromuserid: 2,
    amounts: [{
        amount: 10,
        date: new Date(),
        touserid: 11,

      },
      {
        amount: 10,
        date: new Date(),
        touserid: 11,

      }
    ]
  },
  {
    fromuserid: 3,
    amounts: [{
      amount: 10,
      date: new Date(),
      touserid: 11,

    }]
  }
]

The solution that was given works except that i would like to make it dynamic.

Meaning, in my app, I allow the user to arrange how the array will be sorted.

For example,

const [sort, setSort] = useState('fromuserid')
   const [results, setResults] = useState([])
   <div>
     <select value={sort} onChange={(e)=> setSort(e.target.value)}>
      <option value='fromuserid'>Sort by Buyer</option>
       <option value='touserid'>Sort by Receiver</option>
       <option value='date'>Sort by Date</option>
     </select>
   </div>

then in the:

useEffect(() => {
  allgiftcards.forEach(({
    fromuserid,
    date,
    from,
    giftcardid,
    message,
    template,
    touserid,
    amount,
    paid
  }) => {
    map.has(fromuserid) || map.set(fromuserid, {
      fromuserid,
      cards: []
    })
    map.get(fromuserid).cards.push({
      date,
      from,
      giftcardid,
      message,
      template,
      touserid,
      amount,
      paid
    })
  })
  setResults([...map.values()])
}, [sort])

Here is what i mean by dynamic. If the user selected date, I would like for it to look something like:

useEffect(() => {
  allgiftcards.forEach(({
    fromuserid,
    date,
    from,
    giftcardid,
    message,
    template,
    touserid,
    amount,
    paid
  }) => {
    map.has(date) || map.set(date, {
      date,
      cards: []
    })
    map.get(date).cards.push({
      date,
      from,
      giftcardid,
      message,
      template,
      touserid,
      amount,
      paid
    })
  })
  setResults([...map.values()])
}, [sort])

But It seems to me that having a bunch of if and else statements would be bad coding and would create a lot of extra code, so looking for a nice and clean solution

Upvotes: 1

Views: 471

Answers (2)

Everett
Everett

Reputation: 9568

This really isn't a question of React (or where-ever useEffect comes from). This is really a general Javascript question, and it's a problem that suits itself well for solving with functional programming, or at least, with one of the staples of functional programming: reduce

In this case, you could supply the 2nd argument which is the initial value of the accumulator -- in this example an empty object works well. You can choose any key from the data to bucket the results:

// Choose a key that each object in the set has, e.g. 'fromuserid' or 'touserid'
const group_by = 'fromuserid';

let bucketed = giftcards.reduce(function (acc, x) {
    let pivot = x[group_by]
    let current_vals = (acc.hasOwnProperty(pivot) ? acc[pivot] : []);
    current_vals.push(x);
    acc[pivot] = current_vals;
    return acc
}, {});

console.log(bucketed);

If you really needed to land on the second data structure you shared, you could jostle your initialValue and the exact placement of values into the accumulator, but hopefully this demonstrate the concept of how to dynamically choose how to group the data.

Upvotes: 1

Patricio
Patricio

Reputation: 41

This codepen shows how to dynamically bucket an array of objects based on a specific property. Below is a sample of the callback function you can pass to the reducer -- included in the example.

function groupBy(accum, current, groupKey){
  const val = current[groupKey]
  const {[groupKey]:_, ...content} = current
  if (val in accum){
    accum[val].push(content)
  }
  else accum[val] = [content]
  return accum
}

Upvotes: 1

Related Questions