Abdelhk
Abdelhk

Reputation: 87

How can I return an array of values from an array of grouped objects in js?

I have an array of subscriptions group by the type (basic,medium ...) `

[
  [
    "Basic",
    [
      { "id": 2, "name": "Basic", "started_at": "2022-01-24", "count": 4 },
      { "id": 2, "name": "Basic", "started_at": "2022-03-16", "count": 2 },
      { "id": 2, "name": "Basic", "started_at": "2022-05-16", "count": 1 }
    ]
  ],
  [
    "Medium",
    [
      { "id": 3, "name": "Medium", "started_at": "2022-02-21", "count": 1 },
      { "id": 3, "name": "Medium", "started_at": "2022-05-28", "count": 1 }
    ]
  ],
  [
    "Premium",
    [{ "id": 4, "name": "Premium", "started_at": "2022-04-21", "count": 1 }]
  ],
  [
    "Master",
    [
      { "id": 7, "name": "Master", "started_at": "2022-07-28", "count": 1 },
      { "id": 7, "name": "Master", "started_at": "2022-08-02", "count": 1 }
    ]
  ],
  [
    "Jedi",
    [{ "id": 6, "name": "Jedi", "started_at": "2022-09-28", "count": 1 }]
  ]
]

`

What I want to do is return an array containing objects foreach sub with the following data(get the count value by month): `

[
  {
    label: "Basic",
    data: [4, 0, 2, 0, 1,0],
  },
  {
    label: "Medium",
    data: [0, 1, 0, 0, 1,0],
  },
  ...
]

`

The data field should contain the count field foreach subscription corresponding to the month. For example for with count 4 in January and count 2 in March it will return [4,0,1] with 0 for February.

How can I achieve that ?

I did this but it's returning only the existing month values so there is no way to know which month that value is for.

subscriptions.map((item) => {
            return {
                label: item[0],
                data: item[1].map((value, index) => {
                    return value.count;
                }),
            };
        })

Upvotes: 0

Views: 68

Answers (1)

adiga
adiga

Reputation: 35222

You could reduce the array and create a mapper object which maps each plan with month specifc count. Something like this:

{
  "Basic": {
    "1": 4,
    "3": 2,
    "5": 1
  },
  "Medium": {
    "2": 1,
    "5": 1
  },
  ...
}

Then loop through the entries of the object and create objects with plan as label and an array of length: 12 and get the data for that specific month using the index

const input=[["Basic",[{id:2,name:"Basic",started_at:"2022-01-24",count:4},{id:2,name:"Basic",started_at:"2022-03-16",count:2},{id:2,name:"Basic",started_at:"2022-05-16",count:1}]],["Medium",[{id:3,name:"Medium",started_at:"2022-02-21",count:1},{id:3,name:"Medium",started_at:"2022-05-28",count:1}]],["Premium",[{id:4,name:"Premium",started_at:"2022-04-21",count:1}]],["Master",[{id:7,name:"Master",started_at:"2022-07-28",count:1},{id:7,name:"Master",started_at:"2022-08-02",count:1}]],["Jedi",[{id:6,name:"Jedi",started_at:"2022-09-28",count:1}]]];

const mapper = input.reduce((acc, [plan, subscriptions]) => {
   acc[plan] ??= {}

  for(const { started_at, count } of subscriptions)
    acc[plan][+started_at.slice(5,7)] = count
  
  return acc;
}, {})

const output = 
    Object.entries(mapper)
           .map( ([label, subData]) => ({ 
                label, 
                data: Array.from({ length: 12 }, (_, i) => subData[i+1] ?? 0) 
              }) )
              
console.log(output)

Note:

  • This assumes that the data is for a single year only. If it can be across years you'd have to create another level of nesting:

    {
      "Basic": {
        "2022": {
          "1": 3
        }
      }
    }
    
  • started_at.slice(5,7) is used to get the month number. If the dates are not in the ISO 8601 format, you can use new Date(started_at).getMonth() + 1 to get the month part.

Upvotes: 2

Related Questions