2

I created a general function called unique() to remove duplicates from a specific array.

However I'm facing a problem: I want to build the conditions dynamically based on properties that I pass to the function.

Ex: Let's suppose that I want to pass 2 properties, so I want to check these 2 properties before "remove" that duplicated object.

Currently I'm using eval() to build this condition "&&", however according to my search it's really a bad practice.

So, my question is:

What's the proper way to do this kind of thing?

Below is my current code:

function unique(arr, ...props) {
  const conditions = [];
  for (let prop of props) {
    conditions.push(`element['${prop}'] === elem['${prop}']`);
  }
  const condStr = conditions.join(' && ');

  return arr.filter((element, index) => {
    const idx = arr.findIndex((elem) => {
      return eval(condStr);
    });

    return idx === index;
  });
}

const arr1 = [{
  id: 1,
  name: 'Josh',
  description: 'A description'
}, {
  id: 2,
  name: 'Hannah',
  description: 'A description#2'
}, {
  id: 1,
  name: 'Josh',
  description: 'A description#3'
}, {
  id: 5,
  name: 'Anyname',
  description: 'A description#4'
}];

const uniqueValues = unique(arr1, 'id', 'name');
console.log('uniqueValues', uniqueValues);
dev_054
  • 3,448
  • 8
  • 29
  • 56

2 Answers2

2

This question is a bit subjective as far as implementation details, but the better way if you ask me is to pass in a callback function to hand over to filter.

In doing it this way, you can compose the function anyway you see fit. If you have a complex set of conditions you can use composition to build the conditions in the function before you pass it into your unique function https://hackernoon.com/javascript-functional-composition-for-every-day-use-22421ef65a10

A key to function composition is having functions that are composable. A composable function should have 1 input argument and 1 output value.

The hackernoon article is pretty good and goes much further in depth.

this will return a single function that applies all of your preconditions

function unique(arr, callback) {
  return arr.filter(callback);
}

const compose = (...functions) => data =>
  functions.reduceRight((value, func) => func(value), data)

unique(
    [1, 3, 4, 5 ,7, 11, 19teen]
    compose(
        (someStateCondition) => { /** return true or false **/ }, 
        (result) => { /** return result  === someOtherStateCondition **/}
    )
)
parallaxis
  • 196
  • 1
  • 13
1

Use Array#every to compare all properties inline:

function unique(arr, ...props) {
  return arr.filter((element, index) => {
    const idx = arr.findIndex(
      elem => props.every(prop => element[prop] === elem[prop]);
    );
    return idx === index;
  });
}
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • That's it. Thanks a lot :) – dev_054 Apr 21 '17 at 18:42
  • @Felix Kling Is the '# syntax' in `Array#every` a new idiom? – Ben Aston Apr 21 '17 at 18:49
  • 1
    @Ben: I have seen it used (and used it myself) a couple of times to indicate that `every` is an instance method of arrays. Not sure how common it is (probably not that much if you haven't seen it before ;) ) – Felix Kling Apr 21 '17 at 18:50
  • I saw it used for the first time earlier today I think, and now you are using it. News travels fast. – Ben Aston Apr 21 '17 at 18:51
  • @Ben: To be clear, I've been using this for a while, not just today ;) It's nothing official though as far as I can tell. – Felix Kling Apr 21 '17 at 18:52
  • Ah, understood. – Ben Aston Apr 21 '17 at 18:52
  • 3
    @BenAston It has been around since at least 2010, probably longer: http://stackoverflow.com/questions/2587896/refering-to-javascript-instance-methods-with-a-pound-hash-sign It's a shorthand for `.prototype.`. `Array.prototype.every` → `Array#every`. – JLRishe Apr 21 '17 at 18:56