TGW
TGW

Reputation: 835

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?

Upvotes: 3

Views: 95

Answers (4)

Jorjon
Jorjon

Reputation: 5434

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>

Upvotes: 1

Mohammad Usman
Mohammad Usman

Reputation: 39332

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:

Upvotes: 1

Arman Charan
Arman Charan

Reputation: 5797

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)

Upvotes: 4

Faly
Faly

Reputation: 13356

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);

Upvotes: 1

Related Questions