Zach
Zach

Reputation: 1185

Filter multidimensional array by key

I've looked into using reduce, filter, and map in other examples I've seen on SO, but I'm not sure if they fit my use-case. Say I have the following multidimensional array:

const variableOpts = [
  { id: -1, value: 'Select Variable' },
  { id: 1,
    value: 'Patient',
    options: [
      { id: 2, value: 'First Name', variable: '{first_name}', variableValue: 'Billy' },
      { id: 3, value: 'Last Name', variable: '{last_name}', variableValue: 'Bob' },
      { id: 4, value: 'Office Location', variable: '{office_location}', variableValue: 'Mount Pleasant' },
    ],
  },
  { id: 5, value: 'Another option', variable: '{another_option}', variableValue: 'Test' },
];

What I'd like to do is grab any objects that contain the variable key, whether they are a top-level item, or within an item; like the options array holds. Any help would be greatly appreciated. Thanks!

Update

Ideally I'd like to have an array with everything at one-level so I can loop over them, like this:

[
  { id: 2, value: 'First Name', variable: '{first_name}', variableValue: 'Billy' },
  { id: 3, value: 'Last Name', variable: '{last_name}', variableValue: 'Bob' },
  { id: 4, value: 'Office Location', variable: '{office_location}', variableValue: 'Mount Pleasant' },
  { id: 5, value: 'Another option', variable: '{another_option}', variableValue: 'Test' },
]

Upvotes: 0

Views: 576

Answers (6)

Rick
Rick

Reputation: 1055

You can do something short and strait forward.

const variableOpts = [{ id: -1, value: 'Select Variable'},{id: 1,value: 'Patient',options: [{id: 2,value:'First Name', variable: '{first_name}',variableValue: 'Billy'},{id: 3,value: 'Last Name',variable: '{last_name}',ariableValue: 'Bob'},{ id: 4, value: 'Office Location',variable: '{office_location}',variableValue: 'Mount Pleasant'},],},{id: 5,value: 'Another option',variable: '{another_option}',variableValue: 'Test'},];

var val = variableOpts.filter(item => item.id != -1)
val = val.reduce((a, b) => a.concat(b.options || b), [])
console.log(val);

Upvotes: 1

Will
Will

Reputation: 3241

I'm not sure I really like my answer, but it seems to work.

const variableOpts = [{ id: -1, value: 'Select Variable' }, { id: 1, value: 'Patient', options: [{ id: 2, value: 'First Name', variable: '{first_name}', variableValue: 'Billy' }, { id: 3, value: 'Last Name', variable: '{last_name}', variableValue: 'Bob' }, { id: 4, value: 'Office Location', variable: '{office_location}', variableValue: 'Mount Pleasant' },], }, { id: 5, value: 'Another option', variable: '{another_option}', variableValue: 'Test' },];

let result = [];

let checkArrayEntry = obj => {
    if (obj.hasOwnProperty('variable')) result.push(obj);
    Object.keys(obj).filter(prop => obj[prop] instanceof Array).forEach(entry => obj[entry].forEach(checkArrayEntry));
};

variableOpts.forEach(checkArrayEntry);
console.log(result);

Upvotes: 1

Tesseract
Tesseract

Reputation: 8139

If you want a functional solution using map you could first define a flatten function

function flatten(arr) {
  return [].concat.apply([], arr)
}

function filterArray(arr, f) {
  if(!(arr instanceof Array)) return [];
  return flatten(arr.map(elem =>
    (f(elem)?[elem]:[]).concat(filterArray(elem["options"], f))
  ));
}

Then you can write e.g.

console.log(filterArray(variableOpts, elem => elem["variable"]&&elem["variable"].indexOf("name")>=0));

To get all elements that have the string name somewhere in their variable property.

Upvotes: 0

Shammel Lee
Shammel Lee

Reputation: 4475

You can first filter the objects that either have the options or variable keys, then normalize the results in a for loop…

const variableOpts = [{id: -1, value: 'Select Variable' }, {id: 1, value: 'Patient', options: [{id: 2, value: 'First Name', variable: '{first_name}', variableValue: 'Billy' }, {id: 3, value: 'Last Name', variable: '{last_name}', variableValue: 'Bob' }, {id: 4, value: 'Office Location', variable: '{office_location}', variableValue: 'Mount Pleasant' }]}, {id: 5, value: 'Another option', variable: '{another_option}', variableValue: 'Test' }];

const tempOpts = variableOpts.filter(function(obj){
  return (obj.options && Array.isArray(obj.options)) || obj.variable;
});

const finalOpts = [];

for(let i = 0; i < tempOpts.length; i++){
  let currentOpt = tempOpts[i];

  if(currentOpt.options)
  {
    for(let i = 0; i < currentOpt.options.length; i++){
      finalOpts.push(currentOpt.options[i]);
    }
  }else
  {
    finalOpts.push(currentOpt);
  }
}

console.log(finalOpts);

Upvotes: 2

Jonathan Kuhn
Jonathan Kuhn

Reputation: 15301

Using recursion, you can loop over the array, check each value for the key variable and if found append it to an array of good values. Then check if there is an options key, if found, recurse to check each option. Something like this:

const variableOpts = [{ id: -1, value: 'Select Variable' }, { id: 1, value: 'Patient', options: [{ id: 2, value: 'First Name', variable: '{first_name}', variableValue: 'Billy' }, { id: 3, value: 'Last Name', variable: '{last_name}', variableValue: 'Bob' }, { id: 4, value: 'Office Location', variable: '{office_location}', variableValue: 'Mount Pleasant' }, ], }, { id: 5, value: 'Another option', variable: '{another_option}', variableValue: 'Test' }, ];

function findVariable(arr){
  //output variable
  var out = [];

  //loop over array
  arr.forEach(function(a){
    //if has variable key, add it to output
    if(a.variable){
      out.push(a);
    }

    //if has options, recurse and concat any with variable to output
    if(Array.isArray(a.options)){
      out = out.concat(findVariable(a.options));
    }
  });

  //return the output
  return out;
}

console.log(findVariable(variableOpts));

Upvotes: 2

Washington Guedes
Washington Guedes

Reputation: 4365

You can do a recursive check while populating your resulting array:

const variableOpts=[{id:-1,value:'Select Variable'},{id:1,value:'Patient',options:[{id:2,value:'First Name',variable:'{first_name}',variableValue:'Billy'},{id:3,value:'Last Name',variable:'{last_name}',variableValue:'Bob'},{id:4,value:'Office Location',variable:'{office_location}',variableValue:'Mount Pleasant'}]},{id:5,value:'Another option',variable:'{another_option}',variableValue:'Test'}];

var findObjects = function() {
    var isArray = function(a) {
        return a.map == [].map;
    };
    var isObject = function(o) {
        return Object(o) === o;
    };
    var result = [];
    var stepThrough = function(obj) {
        if (isArray(obj))
            return obj.forEach(stepThrough);

        if (isObject(obj)) {
            for (var key in obj)
                if (isArray(obj[key])) {
                    stepThrough(obj[key]);
                    delete obj[key];
                }
            if (obj.hasOwnProperty('variable'))
                result.push(obj);
        }
    };
    for (var i = 0; i < arguments.length; i++) {
        stepThrough(arguments);
    }
    return result;
};

console.log( findObjects(variableOpts) );

Upvotes: 1

Related Questions