0

I have a json like this and I want to get all the areas that have almost one service that has the selected property equal to true.

{
   "areas": [
      {
         "name": "area 1",
         "services": [
            {
               "label": "srv 1",
               "selected": true
            },
            {
               "label": "srv 2",
               "selected": false
            },
            {
               "label": "srv 3",
               "selected": false
            },
            {
               "label": "srv 4",
               "selected": true
            }
         ]
      },
      {
         "name": "area 2",
         "services": [
            {
               "label": "srv 1",
               "selected": false
            },
            {
               "label": "srv 2",
               "selected": true
            },
            {
               "label": "srv 3",
               "selected": false
            },
            {
               "label": "srv 4",
               "selected": false
            }
         ]
      },
      {
         "name": "area 3",
         "services": [
            {
               "label": "srv 1",
               "selected": false
            },
            {
               "label": "srv 2",
               "selected": false
            },
            {
               "label": "srv 3",
               "selected": false
            }
         ]
      },
      {
         "name": "area 4",
         "services": [
            {
               "label": "srv 1",
               "selected": true
            },
            {
               "label": "srv 2",
               "selected": false
            },
            {
               "label": "srv 3",
               "selected": true
            }
         ]
      }
   ]
}

The result must contains only the areas with only the services with the selected property equal to true and must be pointed out without mutating the original array.

With this code

const result = areas.filter(area =>
  services.some(srv => srv.selected == true)
);

I obtain all the areas, but inside these areas I have all the services (with selected true and with selected false).

This is what I want instead:

{
   "areas": [
      {
         "name": "area 1",
         "services": [
            {
               "label": "srv 1",
               "selected": true
            },
            {
               "label": "srv 4",
               "selected": true
            }
         ]
      },
      {
         "name": "area 2",
         "services": [
            {
               "label": "srv 2",
               "selected": true
            }
         ]
      },
      {
         "name": "area 4",
         "services": [
            {
               "label": "srv 1",
               "selected": true
            },
            {
               "label": "srv 3",
               "selected": true
            }
         ]
      }
   ]
}

Thanks

9 Answers 9

2

To avoid mutating the original object, map each input.areas item to turn it into an object with only truthy selecteds in its services, then filter by whether the services property has any items in it:

const transform = (input) => input.areas
  .map(({ name, services }) => ({
    name,
    services: services.filter(({ selected }) => selected)
  }))
  .filter(({ services }) => services.length);

const input = {
   "areas": [
      {
         "name": "area 1",
         "services": [
            {
               "label": "srv 1",
               "selected": true
            },
            {
               "label": "srv 2",
               "selected": false
            },
            {
               "label": "srv 3",
               "selected": false
            },
            {
               "label": "srv 4",
               "selected": true
            }
         ]
      },
      {
         "name": "area 2",
         "services": [
            {
               "label": "srv 1",
               "selected": false
            },
            {
               "label": "srv 2",
               "selected": true
            },
            {
               "label": "srv 3",
               "selected": false
            },
            {
               "label": "srv 4",
               "selected": false
            }
         ]
      },
      {
         "name": "area 3",
         "services": [
            {
               "label": "srv 1",
               "selected": false
            },
            {
               "label": "srv 2",
               "selected": false
            },
            {
               "label": "srv 3",
               "selected": false
            }
         ]
      },
      {
         "name": "area 4",
         "services": [
            {
               "label": "srv 1",
               "selected": true
            },
            {
               "label": "srv 2",
               "selected": false
            },
            {
               "label": "srv 3",
               "selected": true
            }
         ]
      }
   ]
};
console.log(transform(input));

Sign up to request clarification or add additional context in comments.

Comments

1

You could reduce the array and take only the parts where services have some items after filtering.

var data = { areas: [{ name: "area 1", services: [{ label: "srv 1", selected: true }, { label: "srv 2", selected: false }, { label: "srv 3", selected: false }, { label: "srv 4", selected: true }] }, { name: "area 2", services: [{ label: "srv 1", selected: false }, { label: "srv 2", selected: true }, { label: "srv 3", selected: false }, { label: "srv 4", selected: false }] }, { name: "area 3", services: [{ label: "srv 1", selected: false }, { label: "srv 2", selected: false }, { label: "srv 3", selected: false }] }, { name: "area 4", services: [{ label: "srv 1", selected: true }, { label: "srv 2", selected: false }, { label: "srv 3", selected: true }] }] },
    result = data.areas.reduce((r, o) => {
        var services = o.services.filter(({ selected }) => selected);
        if (services.length) r.push(Object.assign({}, o, { services }));
        return r;
    }, []);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Comments

0

You can use filter twice

const res = data.areas.filter(e => {
    e.services = e.services.filter(s => s.selected);
    return e.services.length;
});

console.log(res);
<script>
const data = {
    "areas": [
    {
        "name": "area 1",
        "services": [
            {
                "label": "srv 1",
                "selected": true
            },
            {
                "label": "srv 2",
                "selected": false
            },
            {
                "label": "srv 3",
                "selected": false
            },
            {
                "label": "srv 4",
                "selected": true
            }
        ]
    },
    {
        "name": "area 2",
        "services": [
            {
                "label": "srv 1",
                "selected": false
            },
            {
                "label": "srv 2",
                "selected": true
            },
            {
                "label": "srv 3",
                "selected": false
            },
            {
                "label": "srv 4",
                "selected": false
            }
        ]
    },
    {
        "name": "area 3",
        "services": [
            {
                "label": "srv 1",
                "selected": false
            },
            {
                "label": "srv 2",
                "selected": false
            },
            {
                "label": "srv 3",
                "selected": false
            }
        ]
    },
    {
        "name": "area 4",
        "services": [
            {
                "label": "srv 1",
                "selected": true
            },
            {
                "label": "srv 2",
                "selected": false
            },
            {
                "label": "srv 3",
                "selected": true
            }
        ]
    }
]
}
</script>

1 Comment

This will mutate the original object, which is often undesirable (it's also probably best for a use of filter to be pure)
0

You can do this

obj.areas.filter(area =>
  area.services = area.services.filter(srv => srv.selected == true)
);

Comments

0

This should be enough, you could basically use a combination of Array#filter.

const filterBySelectedServices = list => list
  .map(area => Object.assign({}, area, {
    services: area.services.filter(service => service.selected),
  }))
  .filter(area => area.services.length);

const data = {
  "areas": [
    {
      "name": "area 1",
      "services": [
        {
          "label": "srv 1",
          "selected": true
        },
        {
          "label": "srv 2",
          "selected": false
        },
        {
          "label": "srv 3",
          "selected": false
        },
        {
          "label": "srv 4",
          "selected": true
        }
      ]
    },
    {
      "name": "area 2",
      "services": [
        {
          "label": "srv 1",
          "selected": false
        },
        {
          "label": "srv 2",
          "selected": true
        },
        {
          "label": "srv 3",
          "selected": false
        },
        {
          "label": "srv 4",
          "selected": false
        }
      ]
    },
    {
      "name": "area 3",
      "services": [
        {
          "label": "srv 1",
          "selected": false
        },
        {
          "label": "srv 2",
          "selected": false
        },
        {
          "label": "srv 3",
          "selected": false
        }
      ]
    },
    {
      "name": "area 4",
      "services": [
        {
          "label": "srv 1",
          "selected": true
        },
        {
          "label": "srv 2",
          "selected": false
        },
        {
          "label": "srv 3",
          "selected": true
        }
      ]
    }
  ]
};

console.log('result', filterBySelectedServices(data.areas));

Comments

0

You can use reduce and filter and take only those element where services have some items after filtering

let data = {"areas": [{"name": "area 1","services": [{"label": "srv 1","selected": true},{"label": "srv 2","selected": false},{"label": "srv 3","selected": false},{"label": "srv 4","selected": true}]},{"name": "area 2","services": [{"label": "srv 1","selected": false},{"label": "srv 2","selected": true},{"label": "srv 3","selected": false},{"label": "srv 4","selected": false}]},{"name": "area 3","services": [{"label": "srv 1","selected": false},{"label": "srv 2","selected": false},{"label": "srv 3","selected": false}]},{"name": "area 4","services": [{"label": "srv 1","selected": true},{"label": "srv 2","selected": false},{"label": "srv 3","selected": true}]}]}

let final = data.areas.reduce((op, inp) => {
  let services = inp.services.filter( ({selected}) => selected )
  if(services.length) op.push({...inp, services})
  return op
},[])

console.log(final)

Comments

0

Try something like this:

const data = {
  "areas": [{
      "name": "area 1",
      "services": [{
          "label": "srv 1",
          "selected": true
        },
        {
          "label": "srv 2",
          "selected": false
        },
        {
          "label": "srv 3",
          "selected": false
        },
        {
          "label": "srv 4",
          "selected": true
        }
      ]
    },
    {
      "name": "area 2",
      "services": [{
          "label": "srv 1",
          "selected": false
        },
        {
          "label": "srv 2",
          "selected": true
        },
        {
          "label": "srv 3",
          "selected": false
        },
        {
          "label": "srv 4",
          "selected": false
        }
      ]
    },
    {
      "name": "area 3",
      "services": [{
          "label": "srv 1",
          "selected": false
        },
        {
          "label": "srv 2",
          "selected": false
        },
        {
          "label": "srv 3",
          "selected": false
        }
      ]
    },
    {
      "name": "area 4",
      "services": [{
          "label": "srv 1",
          "selected": true
        },
        {
          "label": "srv 2",
          "selected": false
        },
        {
          "label": "srv 3",
          "selected": true
        }
      ]
    }
  ]
}

const res = data.areas.map(x => {
  // Destructure the name and services from the current callback object
  const { name, services } = x;

  // Now filter the services object where the .selected property is set to true
  const filteredServices = services.filter(x => x.selected);
  
  if (filteredServices.length) {
    // Project your desired object
    return { name: name, services: filteredServices }
  }
})
// Also filter for empty services
.filter(x => x);

console.log(res);

Comments

0

You could filter inside the callback of a filter, like this:

let data = {"areas": [{"name": "area 1", "services": [{"label": "srv 1", "selected": true}, {"label": "srv 2", "selected": false}, {"label": "srv 3", "selected": false}, {"label": "srv 4", "selected": true}]}, {"name": "area 2", "services": [{"label": "srv 1", "selected": false}, {"label": "srv 2", "selected": true}, {"label": "srv 3", "selected": false}, {"label": "srv 4", "selected": false}]}, {"name": "area 3", "services": [{"label": "srv 1", "selected": false}, {"label": "srv 2", "selected": false}, {"label": "srv 3", "selected": false}]}, {"name": "area 4", "services": [{"label": "srv 1", "selected": true}, {"label": "srv 2", "selected": false}, {"label": "srv 3", "selected": true}]}]};

const result = data.areas.filter(area => {
  // filter area services        
  let selectedServices = area.services.filter(service => {
    return service.selected;
  });

  // If the area has selected services
  if (selectedServices.length) {
    // Save the selected services to area.services
    area.services = selectedServices;
    return true;
  } else {
    // Do not select the area
    return false;
  }
});

console.log(result);

However as pointed out by @CodeManiac this will mutate the original array, if you want to preserve it then use reduce: data.areas.reduce() instead of data.areas.filter()

4 Comments

This mutates original array, you can use map or reduce and filter combination instead
@CodeManiac yes but I don't recall that this was an issue for him? :)
Even though it's not a problem here, Just wanted to point out that It's not considered as good programming practice, you can read this
I'm sorry, I forgot to specify that I need to NOT mutate the original array.. I edit the original question. Thanks
0

After the first filtration you have done, you can filter the services in each index as follows:

result.forEach((item)=>{
     item.services = item.services.filter((service)=>service.selected==true);
})

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.