How to transform this array of records using map/reduce

Hello,

I’ve got an array of record type item that is fetched from a table.

type item = {
  id: int,
  name: string,
  groupName: string,
}

I would like to map it to an array of groupedItems:

type item = {
  id: int,
  name: string
}
type groupedItems = {
  group: string
  items: Js.Array2.t<item>
}

How do I achieve this using map/reduce functions from Js/Belt APIs?

Playground link

type item = {
  id: int,
  name: string,
  groupName: string,
}

type justitem = {
  id: int,
  name: string,
}
type groupedItems = {
  group: string,
  items: Js.Array2.t<justitem>,
}

let a = []

let b = a->Js.Array2.reduce((acc, cur) => {
  let index = acc->Js.Array2.findIndex(g => g.group === cur.groupName)
  let _ = switch index {
  | -1 =>
    acc->Js.Array2.push({
      group: cur.groupName,
      items: [{id: cur.id, name: cur.name}],
    })
  | index => acc[index].items->Js.Array2.push({id: cur.id, name: cur.name})
  }
  acc
}, [])


Imho, there’s really no need for map/reduce, this can be done with good old JS Array forEach:

module Input = {
  type item = {id: int, name: string, groupName: string}
}

module Output = {
  type item = {id: int, name: string}
  type groupedItems = {group: string, items: array<item>}

  let item = ({Input.id: id, name}) => {id, name}
}

let group = items => {
  module Arr = Js.Array2
  module Dict = Js.Dict
  let map = Dict.empty()

  items->Arr.forEach(item => {
    let name = item.Input.groupName

    switch map->Dict.get(name) {
    | Some({Output.items, _}) =>
      items->Arr.push(Output.item(item))->ignore
    | None =>
      map->Dict.set(name, {Output.group: name, items: [Output.item(item)]})
    }
  })

  map->Dict.values
}

This is using an object as a map of group names to grouped items, and mutating the group arrays internally, but the mutation is not visible to the outside world. At the end we just return an array of groupedItems that was required.

2 Likes

I’m accepting @yawaramin’s answer because it’s more readable and more approachable for incoming JS developers, even though I had asked for a Belt API based answer.

1 Like