3

I am trying to display data using SectionList in React Native. I have written the code below displaying what I am trying to accomplish.

I want the data to first be grouped together by date, and inside of that date, I need them grouped by location. A regular JavaScript solution will work. It's important that it has a title and data key.

My input data is in this format:

[ { game_id: 1171,
    date: '2018-11-17',
    location: 'Plaza'
   },
  { game_id: 1189,
    date: '2018-11-17',
    location: 'Field - Kickball'
   },
   { game_id: 1489,
    date: '2018-11-16',
    location: 'Field - Kickball'
   },
   { game_id: 1488,
    date: '2018-11-16',
    location: 'Field - Soccer'
   }
]

I need the obtain the following output from the data array above:

data = [{
    title: "2018-11-17",
    data: [{
            title: "Field - Kickball",
            data: [{
                game_id: 1189,
                date: '2018-11-17',
                location: 'Field - Kickball'
            }]
        },
        {
            title: "Plaza",
            data: [{
                game_id: 1171,
                date: '2018-11-17',
                location: 'Plaza'
            }]
        }
    ]
    },
    {
        title: "2018-11-16",
        data: [{
                title: "Field - Kickball",
                data: [{
                    game_id: 1489,
                    date: '2018-11-16',
                    location: 'Field - Kickball'
                }]
            },
            {
                title: "Field - Soccer",
                data: [{
                    game_id: 1488,
                    date: '2018-11-16',
                    location: 'Field - Soccer'
                }]
            }
        ]
    }
]

I have tried this already:

const games = [data here]
var groups = _(games)
.groupBy(x => x.date)
        .map(value => {
            return _.groupBy(value, 'location')
            .map(({key, value}) => ({title: key, data: value}))
        })

        .map((value, key) => {
            return ({title: value[Object.keys(value)[0]][0].date, data: value})
        })

4 Answers 4

3

There are a few ways this can be achieved, however a simple approach that does not require third party dependencies like Underscore or Lodash, can implemented with the built-in Array#reduce() method as shown below.

Please see the documentation in the following code snippet for details on how this solution works:

const input =  [ { game_id: 1171, date: '2018-11-17', location: 'Plaza' }, { game_id: 1189, date: '2018-11-17', location: 'Field - Kickball' }, { game_id: 1489, date: '2018-11-16', location: 'Field - Kickball' }, { game_id: 1488, date: '2018-11-16', location: 'Field - Soccer' } ];


/* Reduce input data to required nested sub-array structure */
const data = input.reduce((result, item) => {

  /* Construct item to be inserted into sub array for this item.date
  in resulting data object */
  const resultItem = {
    title: item.location,
    data: [{
      game_id: item.game_id,
      date: item.date,
      location: item.location
    }]
  };

  /* Find existing item in result array with title that matches date */
  const resultDateList = result.find(i => i.title === item.date);

  if (resultDateList) {

    /* If matching sublist found, add constructed item to it's data array */
    resultDateList.data.push(resultItem);
  } else {

    /* If not matching sublist found, add a new one to the result for this item
    and pre-populate the data array with new item*/
    result.push({
      title: item.date,
      data: [resultItem]
    });
  }

  return result;

}, [])

console.log(data)

Hope that helps!

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

1 Comment

Actually, this is not exacly right, I guess I forgot to include that edge case. There could be multiple games at that time and location, which has to be included. Meaning there can be a game happening on 2019-11-17 at Plaza, but your current setup doesnt include those
2

If you want a standard solution, then you could first reduce to an object, and return the values of that object, and then group again on the output :)

function groupBy( arr, prop ) {
  return Object.values( arr.reduce( ( aggregate, item ) => {
    const val = item[prop];
    if (!aggregate[val]) {
      aggregate[val] = {
        [prop]: val,
        data: []
      };
    }
    aggregate[val].data.push( item );
    return aggregate;
  }, {} ) );
}

const games = [ { game_id: 1171,
    date: '2018-11-17',
    location: 'Plaza'
   },
  { game_id: 1189,
    date: '2018-11-17',
    location: 'Field - Kickball'
   },
   { game_id: 1489,
    date: '2018-11-16',
    location: 'Field - Kickball'
   },
   { game_id: 1488,
    date: '2018-11-16',
    location: 'Field - Soccer'
   }
];

const grouped = groupBy( games, 'date' )
  .map( item => ({ ...item, data: groupBy( item.data, 'location' ) }) );
  
console.log( grouped );

Note that I just use the prop that got extracted as a target property for the grouping, if you want title instead, just change the [prop]: val to 'title': val and then you can make your second grouping a litle bit easier :)

Comments

2

You can do something like this with ES6 and without lodash:

let arr = [ { game_id: 1171, date: '2018-11-17', location: 'Plaza' }, { game_id: 1189, date: '2018-11-17', location: 'Field - Kickball' }, { game_id: 1489, date: '2018-11-16', location: 'Field - Kickball' }, { game_id: 1488, date: '2018-11-16', location: 'Field - Soccer' } ]

let groupByfield = (data, field) => data.reduce((r, c) => {
  let key = c[field]
  r[key] = r[key] || {title: key, data: []}
  r[key].data = [...(r[key].data || []), c]	
  return r
}, {})

let result = Object.values(groupByfield(arr, 'date'))
  .map(x => ({ 
    title: x.title, 
    data: Object.values(groupByfield(x.data, 'location'))
   })
)

console.log(result)

The idea is to create your custom groupBy function and then use it for your groupings.

We are using Array.reduce, Array.map and Object.values

1 Comment

Is there an error here? It seems the nested data object is a map (with keys like Plaza) rather than an array?
1

Generate a function using _.flow() that can group an array by a field, and convert it to the format of { title, data }. The function should also accept a transformer for the data. Now you can use it recursively to group multiple times.

const { identity, flow, partialRight: pr, groupBy, map } = _

const groupByKey = (key, transformer = identity) => flow(
  pr(groupBy, key),
  pr(map, (data, title) => ({
    title,
    data: transformer(data)
  }))
)

const data = [{"game_id":1171,"date":"2018-11-17","location":"Plaza"},{"game_id":1189,"date":"2018-11-17","location":"Field - Kickball"},{"game_id":1489,"date":"2018-11-16","location":"Field - Kickball"},{"game_id":1488,"date":"2018-11-16","location":"Field - Soccer"}]

const result = groupByKey('date', groupByKey('location'))(data)

console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

And the same idea with lodash/fp:

const { identity, flow, groupBy, map, get } = _

const groupByKey = (key, transformer = identity) => flow(
  groupBy(key),
  map(data => ({
    title: get([0, key], data),
    data: transformer(data)
  }))
)

const data = [{"game_id":1171,"date":"2018-11-17","location":"Plaza"},{"game_id":1189,"date":"2018-11-17","location":"Field - Kickball"},{"game_id":1489,"date":"2018-11-16","location":"Field - Kickball"},{"game_id":1488,"date":"2018-11-16","location":"Field - Soccer"}]

const result = groupByKey('date', groupByKey('location'))(data)

console.log(result)
<script src='https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)'></script>

Comments

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.