JavaScript: как сгруппировать вложенный массив - PullRequest
3 голосов
/ 20 мая 2019

Я пытаюсь отобразить данные, используя SectionList в React Native.Я написал код ниже, отображающий то, что я пытаюсь достичь.

Я хочу, чтобы данные сначала были сгруппированы по date, а внутри этой даты они должны быть сгруппированы по местоположению.Обычное решение JavaScript будет работать.Важно, чтобы он имел клавиши title и 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'
   }
]

Мне нужно получить следующий вывод из массива данных выше:

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

Я уже пробовал это:

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 ]

3 голосов
/ 20 мая 2019

Есть несколько способов, которыми это может быть достигнуто, однако простой подход, который не требует сторонних зависимостей, таких как Underscore или Lodash, может быть реализован с помощью встроенного метода Array # redu () , как показанониже.

См. документацию в следующем фрагменте кода для получения подробной информации о том, как работает это решение:

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)

Надеюсь, это поможет!

2 голосов
/ 20 мая 2019

Если вы хотите стандартное решение, то вы можете сначала преобразовать его в объект и вернуть значения этого объекта, а затем снова сгруппировать на выходе:)

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

Обратите внимание, что я просто использую опору, извлеченную в качестве целевого свойства для группировки, если вы хотите вместо title, просто измените [prop]: val на 'title': val и тогда вы можете немного облегчить свою вторую группировку:)

2 голосов
/ 20 мая 2019

Вы можете сделать что-то подобное с ES6 и без 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)

Идея состоит в том, чтобы создать пользовательскую функцию groupBy и затем использовать ее для своих группировок.

Мы используем Array.reduce , Array.map и Object.values ​​

1 голос
/ 20 мая 2019

Создайте функцию, используя _.flow(), которая может сгруппировать массив по полю, и преобразовать его в формат {title, data}.Функция также должна принимать преобразователь для данных.Теперь вы можете использовать его рекурсивно для группировки несколько раз.

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>

И та же идея с 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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...