1
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

1
  • I actually think you made your life much more complicated putting your data in that new format. It would have been a lot simpler to filter and sort on properties like date or touserid in the original set, and then create some new data based on that new sort. Commented Jun 23, 2021 at 1:04

2 Answers 2

1

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.

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

4 Comments

How would you go about doing it if it were to be sorted by date it was purchased (1 day frame)
Remember: as developers we don't solve complex problems. We divide things up into simple problems, and then solve those. So the first step would be to add a new stubbed date field, e.g. one holding only YYYY-MM-DD, and then group by that field. You could do this using JS's map function to prep the data for the reduce example in the post.
I see that the format is in an object like this: {id: Array(n), ...}, how do I access the id?
WOuld it be possible to transform the bukcted in an array format?
1

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
}

1 Comment

In my case, I'm using a firebase timestamp as the date, how would i convert the date before its value is given

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.