Luiz Alves
Luiz Alves

Reputation: 2645

Filtering an array of objects with javascript

I have a menu array called "items". It has submenus.

I need to filter the items with property "visible" equal 1. I don't know at run time how deep the hierarchy will be. I need a function returning a new array, with the next conditions:

  1. Every non-matching object with no children, or no matches in children hierarchy, should not exist in output object
  2. Every object with a descendant that contains a matching object, should remain
  3. Only descendants with matching objects should remain

My question is similar to this post Recursively filter array of objects.

But it does not work to me. I have used the following function, but it´s not working:

const items = [ { icon: "mdi-view-dashboard", title: "Dashboard", to: "/", visible: 1, }, 
{ title: "Manutenção", icon: "mdi-hammer-screwdriver", to: "", visible: 1, 
 items: [ 
    { title: "Usuários", icon: "mdi-account", to: "/usuarios", visible: 1, }, 
    { title: "Cores", to: "", visible: 1, 
     items: [ 
       { title: "Cores da Fila", icon: "mdi-eyedropper-variant", to: "/coresfila", visible: 1, },
       { title: "Cores da Agenda", icon: "mdi-palette", to: "/coresagenda", visible: 1, }, 
       ], 
     }, 
    { title: "Tabelas Médicas", to: "", visible: 1, 
     items: [ 
       { title: "Convênios", icon: "mdi-clipboard-outline", to: "/convenios", visible: 1, }, 
       { title: "Planos", icon: "mdi-plus-box", to: "/planos", visible: 1, }, 
       { title: "Especialidades", icon: "mdi-format-font", to: "/especialidadescompletas", visible: 1, }, 
       { title: "Modelos de Atestados", icon: "mdi-account-details-outline", to: "/modelosAtestados", visible: 1, }, 
       {  title: "Modelos de Prescrições", icon: "mdi-account-edit-outline",  to: "/modelosPrescricoes",  }, 
       { title: "Cid-10", icon: "mdi-alphabetical", to: "/cid10", visible: 1, }, 
       { title: "Procedimentos", icon: "mdi-alarm-plus", to: "/procedimentos", visible: 1, }, 
       { title: "Materiais", icon: "mdi-table-of-contents", to: "/materiais", visible: 1, },
       { title: "Medicamentos", icon: "mdi-water", to: "/medicamentos", visible: 1, }, 
       { title: "Taxas", icon: "mdi-cash-100", to: "/taxas", visible: 1, }, 
      ], 
    }, 
  ], 
}, 
{ title: "Empresa", icon: "mdi-cash-100", to: "", visible: 1, 
 items: [ { title: "Perfil da Empresa", icon: "mdi-account-network-outline", to: "/perfilempresa", visible: 1, }, 
 { title: "Créditos SMS", icon: "mdi-cash-usd-outline", to: "/creditossms", visible: 1, }, 
 ], 
}, 
{ title: "Clientes", icon: "mdi-account-alert-outline", to: "/clientes", visible: 1, }, 
{ title: "Agenda", icon: "far fa-calendar-check", to: "/agenda", visible: 1, }, 
{ title: "Fila", icon: "mdi-account-multiple-check", to: "/fila", visible: 1, }, 
{ title: "Atendimento Médico", icon: "fas fa-user-md", to: "/atendimento", visible: 1, }, 
{ title: "Tela de Chamadas", icon: "mdi-play-network-outline", to: "/telao", visible: 1, }, 
{ title: "DICOM", icon: "mdi-radioactive-off", to: "/dicom", visible: 1, }, 
{ title: "Estatísticas", icon: "mdi-chart-box", to: "", visible: 1, 
items: [ { title: "Clientes", icon: "mdi-account-arrow-right", to: "", visible: 1, 
 items: [ { title: "Por convênios", icon: "mdi-poll", to: "/estat_cliente_por_convenios", visible: 1, }, 
 { title: "Por mês", icon: "mdi-poll", to: "/estat_cliente_por_mes", visible: 1, }, 
 ], 
}, 
{ title: "Faturamento", icon: "mdi-cash-usd", to: "", visible: 1, 
 items: [ { title: "Por convênios", icon: "mdi-poll", to: "/estat_faturamento_por_convenios", visible: 1, }, 
 { title: "Por mês", icon: "mdi-poll", to: "/estat_faturamento_por_mes", visible: 1, }, 
], }, 
], }, 
{ title: "Autorizações", icon: "mdi-microphone-variant", to: "/listaautorizacoes", visible: 1, }, 
{ title: "Faturamento", icon: "mdi-cash-usd", to: "", visible: 1, 
items: [ { title: "Nova Guia", icon: "mdi-cart-plus", to: "/guiasfaturas", visible: 0, }, 
{ title: "Lista Guias", icon: "mdi-tray-plus", to: "/listaguias", visible: 1, }, 
{ title: "Lote de Guias", icon: "mdi-bag-personal", to: "/loteguias", visible: 1, }, ], }, 
] 


function ofilter(arr) {
  var matches = [];
  if (!Array.isArray(arr)) return matches;

  arr.forEach(function(i) {
    if (i.visible && i.visible === 1) {
      matches.push(i);
    } else {
      let childResults = this.ofilter(i.items);
      if (childResults.length)
        matches.push(Object.assign({}, i, {
          items: childResults
        }));
    }
  });
  return matches;
}
console.log(ofilter(items))

Upvotes: 1

Views: 140

Answers (2)

Lionel Rowe
Lionel Rowe

Reputation: 5956

Here's a recursive filter based on a recursive visibility check. This version preserves the nested structure:

const isVisible = item => item.visible
    || item.items?.some(isVisible)

const filterItems = items => {
    items.forEach(item => {
        if (item.items) item.items = filterItems(item.items)
    })

    return items.filter(isVisible)
}

console.log(filterItems(
    [
        { id: 'a', visible: 1 },
        { id: 'b', visible: 0 },
        {
            id: 'c',
            visible: 0,
            items: [
                { id: 'd', visible: 1 },
                { id: 'e', visible: 0 }
            ]
        },
        { id: 'f', visible: 1, items: [{ id: 'g', visible: 0 }] },
        { id: 'h', visible: 0, items: [{ id: 'i', visible: 0 }] },
    ]
))

Alternatively, here's a version that returns a flat array:

const filterItemsFlat = (items, results = []) => {
    items.forEach(item => {
        if (item.items) filterItemsFlat(item.items, results)
        if (item.visible) results.push(item)
    })

    results.forEach(r => delete r.items)

    return results
}

console.log(filterItemsFlat(
    [
        { id: 'a', visible: 1 },
        { id: 'b', visible: 0 },
        {
            id: 'c',
            visible: 0,
            items: [
                { id: 'd', visible: 1 },
                { id: 'e', visible: 0 }
            ]
        },
        { id: 'f', visible: 1, items: [{ id: 'g', visible: 0 }] },
        { id: 'h', visible: 0, items: [{ id: 'i', visible: 0 }] },
    ]
))

Upvotes: 1

Darlan Dieterich
Darlan Dieterich

Reputation: 2537

Another alternative to question:

 const items = [ 
  { icon: "mdi-view-dashboard", title: "Dashboard", to: "/", visible: 1, }, 
  { title: "Manutenção", icon: "mdi-hammer-screwdriver", to: "", visible: 0, 
   items: [ 
     { title: "Usuários", icon: "mdi-account", to: "/usuarios", visible: 1, }, 
     { title: "Cores", to: "", visible: 0}
   ]
  }
];

function ofilter(arr) {
  let r = [];     
  arr.forEach((i) => {
    if (i.visible == 1) {
      r.push(i);
    } 
    if (i.items) {  
      r.push(ofilter(i.items))
    }    
  })
  return r;
}

console.log('result:', ofilter(items))

Upvotes: 0

Related Questions