Search code examples
javascripttypescriptfunctional-programmingramda.js

counting member from nested array of objects using ramda


[
    {
        'members': {
            'infants': [{ 'name': 'A' }],
            'adults': [{ 'name': 'B' }, { 'name': 'C' }],
            'children': [{ 'name': 'D' }],
        },
    },
    {
        'members': {
            'infants': [],
            'adults': [{ 'name': 'F' }, { 'name': 'G' }, { 'name': 'H' }],
            'children': [],
        },
    },
]

Here is how my data object looks like

and I tried to implement it like

const travelerCounter = compose(
  length,
  unnest,
  unnest,
  map(values),
  unnest,
  pluck('members')
)

I am not sure if it is a good solution or we can write it in compact way. It also shows the correct number but if I try to use it in TypeScript I am a TS error ts(2769)


Solution

  • You can use R.chain with R.values and then flatten to a single array:

    const { compose, length, flatten, chain, values, pluck } = R
    
    const fn = compose(
      length, // get the length
      flatten, // flatten to a single array
      chain(values), // combine the values of all member objects
      pluck('members') // get members objects
    )
    
    const data = [{"members":{"infants":[{"name":"A"}],"adults":[{"name":"B"},{"name":"C"}],"children":[{"name":"D"}]}},{"members":{"infants":[],"adults":[{"name":"F"},{"name":"G"},{"name":"H"}],"children":[]}}]
    
    const result = fn(data)
    
    console.log(result)
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js" integrity="sha512-t0vPcE8ynwIFovsylwUuLPIbdhDj6fav2prN9fEu/VYBupsmrmk9x43Hvnt+Mgn2h5YPSJOk7PMo9zIeGedD1A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

    TS types:

    type Members = { members: Record<string, { name: string }[]> };
    
    const fn: (v: Members[]) => number = compose(
      length, // get the length
      flatten, // flatten to a single array
      chain(values), // combine the values of all member objects
      pluck("members") // get members objects
    );
    

    Instead of flattening, you can also pluck the lengths, and then sum them:

    const { compose, sum, pluck, length, chain, values } = R
    
    const fn = compose(
      sum, // sum the lengths
      pluck('length'), // get the length of each members array
      chain(values), // combine values of all members objects
      pluck('members') // get members objects
    )
    
    const data = [{"members":{"infants":[{"name":"A"}],"adults":[{"name":"B"},{"name":"C"}],"children":[{"name":"D"}]}},{"members":{"infants":[],"adults":[{"name":"F"},{"name":"G"},{"name":"H"}],"children":[]}}]
    
    const result = fn(data)
    
    console.log(result)
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js" integrity="sha512-t0vPcE8ynwIFovsylwUuLPIbdhDj6fav2prN9fEu/VYBupsmrmk9x43Hvnt+Mgn2h5YPSJOk7PMo9zIeGedD1A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

    TS types - in this case TS can't infer the types inside R.compose, so we'll need to provide a more verbose signature:

    type Member = { name: string };
    type MembersRecords = Record<string, Member[]>;
    type Members = { members: MembersRecords };
    
    const fn = compose<[Members[]], MembersRecords[], Member[][], number[], number>(
      sum, // sum the lengths
      pluck('length'), // get the length of each members array
      chain(values), // combine values of all members objects
      pluck('members') // get members objects
    )