happygopher
happygopher

Reputation: 121

How to process JSON fields with lodash map/countBy/groupBy?

I have the following json to process:

[
  {
    "id": "device1",
    "name": "dev1",
    "status": "online",
    "pool": "pool1",
    "ports": [
       {
          "id": "port1",
          "status": "online"
       },
       {
          "id": "port2",
          "status": "offline"
       },
       {
          "id": "port3",
          "status": "online"
       }
    ]
  },
  {
    "id": "device2",
    "name": "dev2",
    "status": "online",
    "pool": "pool2",
    "ports": [
       {
          "id": "port1",
          "status": "offline"
       },
       {
          "id": "port2",
          "status": "offline"
       },
       {
          "id": "port3",
          "status": "online"
       }
    ]
  }
]

And I need to count the number of ports for each pool, I was thinking about something like this:

inside devices.jsx:

fetchDevices(props) {
   Promise.all([
      getDevices()  //=> This func fetchs and return the json
   ]).then(results => {
      let devices = flatten(results)

      if (props.filter) {
        devices = filter(devices, this.props.filter)
      }

      let pools = chain(devices)
         .groupBy('pool')
         .map((value, key) => ({
            name: key,
            devices: chain(value).filter(device => ['online','offline'].includes(device.status)).value()
         }))
         .values()
         .map(pool => Object.assign(pool,
           {
             ports: countBy(pool.devices, 'ports'),
             portsCount: pool.devices.reduce((memo, device) => memo + device.ports, 0)
           })
         )
         .value()
   }
   ...
}

Later, on the code I try to display the portsCount:

<div><span className="value">{pool.portsCount}</span> {pool.portsCount === 1 ? 'port' : 'ports'}</div>

when I run the code I'm getting no value at all, is the code ok? or am I missing something here?

basically I want to process:

var arr = [
{
  "id":"device1",
  "pool":"pool1",
  "ports": [
    {
      "id": "port1",
      "status": "online"
    },
    {
      "id": "port2",
      "status": "offline"
    }
  ]
},
{
  "id":"device2",
  "pool":"pool2",
  "ports": [
    {
      "id": "port1",
      "status": "offline"
    }
  ]
}
];

and with groupBy and .map get the portsCount for each pool, any ideas?

Upvotes: 0

Views: 610

Answers (3)

Alexander Staroselsky
Alexander Staroselsky

Reputation: 38787

The first issue is that the reduce for portsCount is not correct. You aren't adding/returning a number, use the length property of ports for that respective pool:

let pools = _.chain(data)
  .groupBy("pool")
  .map((value, key) => ({
    name: key,
    devices: _.chain(value)
      .filter(device => ["online", "offline"].includes(device.status))
      .value()
  }))
  .values()
  .map(pool =>
    Object.assign(pool, {
      ports: _.countBy(pool.devices, "ports"),
      portsCount: pool.devices.reduce((memo, device) => {
        return memo + device.ports.length;
      }, 0)
    })
  )
  .value();

Then in the react code you can loop over like this:

<div>
  {this.state.pools.map(pool => (
    <div>
      <span className="value">{pool.portsCount}</span>{" "}
      {pool.portsCount === 1 ? "port" : "ports"}
    </div>
  ))}
</div>

Here is an example in action.

Upvotes: 1

Ori Drori
Ori Drori

Reputation: 191976

GroupBy the pool, and map the the groups to an object. You can combine the groups' ports to an array using _.flatMap() with _.reject() to remove null values. To get the total count of the groups' ports you can use _.sumBy():

const arr = [{"id":"device1","pool":"pool1","ports":[{"id":"port1","status":"online"},{"id":"port2","status":"offline"}]},{"id":"device2","pool":"pool2","ports":[{"id":"port1","status":"offline"}]}, {"id":"device1","pool":"pool1","ports": null}];

const result = _(arr)
  .groupBy('pool')
  .map((g, name) => ({
    name,
    ports: _(g).flatMap('ports').reject(_.isNil).value(), // combine the ports arrays into a single array and remove null values
    portCount: _.sumBy(g, 'ports.length') // count the total ports
  }))
  .value();

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

Upvotes: 1

albert
albert

Reputation: 474

const result = devices.map(device => ({
  [device.pool]: device.ports.length
}))
console.log(result)

https://repl.it/repls/KaleidoscopicLazyBase

Upvotes: 1

Related Questions