简体   繁体   中英

Remove element from an array based on it's multiple peoperties

I've an array of elements as follows

entities

[
  {
    "name":"tiger",
    "imageurl":"https://someurl.com",
    "type":"animal"
  },
  {
    "name":"cat",
    "imageurl":"https://someurl.com",
    "type":"animal"
  },
{
    "name":"parrot",
    "imageurl":"https://someurl.com",
    "type":"bird"
  },{
    "name":"potato",
    "imageurl":"https://someurl.com",
    "type":"vegetable"
  },
  {
    "name":"orange",
    "imageurl":"https://someurl.com",
    "type":"fruit"
  },
  {
    "name":"orange",
    "imageurl":"https://someurl.com",
    "type":"colour"
  }
]

I've another array which is as follows

elemToRemove

[orange@fruit,cat@animal,tiger@animal]

I want to remove the elements having name=orange and type=fruit , name=cat and type=animal , name=tiger and type=animal

It is easily possible to remove the element based on single property by using filter over the array but in this case I am not able to put up map/filter/reduce to remove these elements.

I used split to create a name and type array and tried to do this but as we've type repeating the condition always returned false.

 let nameArray = elemToRemove.map(function (elem) {
    return elem.split('@')[0];
  });

  let typeArray= elemToRemove.map(function (elem) {
    return elem.split('@')[1];
  });

  var reqData= entities.filter(function (obj) {
    return (nameArray.indexOf(obj.name) === -1 && typeArray.indexOf(obj['env']) === -1);
  }); 

Thus always giving me an empty reqData array. I do not have a provision to have an id or else I could've used id to delete the elements.

Expected Output

[
    {
        "name":"parrot",
        "imageurl":"https://someurl.com",
        "type":"bird"
      },{
        "name":"potato",
        "imageurl":"https://someurl.com",
        "type":"vegetable"
      },
      {
        "name":"orange",
        "imageurl":"https://someurl.com",
        "type":"colour"
      }
    ]

What is most elegant way to achieve this?

Map tends to be useful for these sorts of problems with the bonus of sublinear value retrieval.

 // Input. const input = [{"name":"tiger","imageurl":"https://someurl.com","type":"animal"},{"name":"cat","imageurl":"https://someurl.com","type":"animal"},{"name":"parrot","imageurl":"https://someurl.com","type":"bird"},{"name":"potato","imageurl":"https://someurl.com","type":"vegetable"},{"name":"orange","imageurl":"https://someurl.com","type":"fruit"},{"name":"orange","imageurl":"https://someurl.com","type":"colour"}] // Tags. const tags = ["orange@fruit", "cat@animal", "tiger@animal"] // Clean. const clean = (array, tags) => { const map = new Map(array.map(x => [`${x.name}@${x.type}`, x])) // Create Map. tags.forEach(tag => map.delete(tag)) // Remove each tag from Map. return Array.from(map.values()) // Return Array from Map.values(). } // Output. const output = clean(input, tags) // Proof. console.log(output) 

You can use filter() to select only those objects which doesn't match desired criteria. We will test each object using .some() to find if there any match found between object and the array having strings to check.

 let data = [{"name":"tiger", "imageurl":"https://someurl.com", "type":"animal"}, {"name":"cat", "imageurl":"https://someurl.com", "type":"animal"}, {"name":"parrot", "imageurl":"https://someurl.com", "type":"bird"}, { "name":"potato", "imageurl":"https://someurl.com", "type":"vegetable"}, { "name":"orange", "imageurl":"https://someurl.com", "type":"fruit"}, { "name":"orange", "imageurl":"https://someurl.com", "type":"colour"}]; let arr = ['orange@fruit', 'cat@animal', 'tiger@animal']; let result = data.filter(o => !arr.some(s => ( [name, type] = s.split('@'), o['name'] === name && o['type'] === type ))); console.log(result); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 

Docs:

You can use array.filter :

 var arr = [ {"name":"tiger","imageurl":"https://someurl.com","type":"animal"}, {"name":"cat","imageurl":"https://someurl.com","type":"animal"}, {"name":"parrot","imageurl":"https://someurl.com","type":"bird"}, {"name":"potato","imageurl":"https://someurl.com","type":"vegetable"}, {"name":"orange","imageurl":"https://someurl.com","type":"fruit"}, {"name":"orange","imageurl":"https://someurl.com","type":"colour"} ]; var toRemove = ['orange@fruit', 'cat@animal', 'tiger@animal']; var filterOut = toRemove.map(e => { var [name, type] = e.split('@'); return {name, type}; }); arr = arr.filter(e => !filterOut.find(({name, type}) => e.name === name && e.type === type)); console.log(arr); 

If your definition of elegant is to have the least possible code (to avoid human error), and reutilize elements that have been already created by others, I recommend using an external library like Lodash that already have a function to do this.

The first part if a bit complex since I'm parting from having a string:

[orange@fruit,cat@animal,tiger@animal]

that needs to be parsed, instead of having already an array of values like the other answers.

// First we need to convert the filter to a proper Json representation.
// This is needed since the _.remove function takes a Json object.
// This could be simplified if your filter string were already a
// Json object.
var filter = "[orange@fruit,cat@animal,tiger@animal]";
filter = filter.replace(/(\w+)@(\w+)[,\]]/g, (m, p1, p2, offset, string) => {
    return `{"name":"${p1}","type":"${p2}"}${m.includes(']')?']':','}`;
});
filter = JSON.parse(filter);


// Next, apply the filter to the remove function from Lodash.
// Once you have a Json object, it's only two lines of code.
const rm = _.partial(_.remove, obj);
filter.forEach(rm)

 var obj = [ { "name":"tiger", "imageurl":"https://someurl.com", "type":"animal" }, { "name":"cat", "imageurl":"https://someurl.com", "type":"animal" }, { "name":"parrot", "imageurl":"https://someurl.com", "type":"bird" },{ "name":"potato", "imageurl":"https://someurl.com", "type":"vegetable" }, { "name":"orange", "imageurl":"https://someurl.com", "type":"fruit" }, { "name":"orange", "imageurl":"https://someurl.com", "type":"colour" } ]; // First we need to convert the filter to a proper Json representation. // This is needed since the _.remove function takes a Json object. // This could be simplified if your filter string were already a // Json object. var filter = "[orange@fruit,cat@animal,tiger@animal]"; filter = filter.replace(/(\\w+)@(\\w+)[,\\]]/g, (m, p1, p2, offset, string) => { return `{"name":"${p1}","type":"${p2}"}${m.includes(']')?']':','}`; }); filter = JSON.parse(filter); // Next, apply the filter to the remove function from Lodash. // Once you have a Json object, it's only two lines of code. const rm = _.partial(_.remove, obj); filter.forEach(rm) console.log(obj); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.js"></script> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM