1

I created a function to check if there are repeated cell phone numbers in a list. The problem is that I did this by using nested for. How could I optimize this code with functional programming ?

  checkDuplicate(): boolean {

    for (let i = 0; i < this.phoneList.length; i++) {
      for (let j = 0; j < this.phoneList.length; j++) {
        if (i != j) {
            if (this.phoneList[i].number === this.phoneList[j].number) {
              this.toastrService.error('Phone already in List!');
              return true;
            }
        }
      }
    }

    return false;
  }
Andre
  • 431
  • 7
  • 23
  • checkout `Array.filter()` – HolyMoly Nov 24 '19 at 17:39
  • Optimal will also depend upon the actual data and browser since on very much older browsers a negative `while(this.phoneList.length--)` will be faster – Mark Schultheiss Nov 24 '19 at 19:57
  • Does this answer your question? [Remove duplicates from an array of objects in JavaScript](https://stackoverflow.com/questions/2218999/remove-duplicates-from-an-array-of-objects-in-javascript) – Heretic Monkey Nov 24 '19 at 21:42

6 Answers6

4

You can make a Set containing only the unique numbers and compare the length of the Set to the length of the original array

hasDuplicates(): boolean {
  return new Set(this.phoneList.map(p => p.number)).size < this.phoneList.length
}
Asaf Aviv
  • 11,279
  • 1
  • 28
  • 45
2

O(n) solution

It's not a functional but it's the fastest so far.

const checkDuplicate = (phones)=> {
    let counts = {};
    
    for(let phone of phones) {
        if(counts[phone.number]) return true;
        counts[phone.number] = 1;
    }

    return false;
}

if(checkDuplicate(this.phoneList)) {
  this.toastrService.error('Phone already in List!');
}
Community
  • 1
  • 1
Temo Tchanukvadze
  • 1,513
  • 6
  • 16
1

even better than filter (which i suggested in a comment) use Set - there are a few ways to do it but this is pretty clean. However .filter() would probably be considered more 'functional' as it is a HOC

let a = [1,2,1,3,3,5]
let x = [...new Set(a)]

// => [1, 2, 3, 5]

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set

HolyMoly
  • 2,020
  • 3
  • 22
  • 35
  • 1
    Clean indeed, but it seems from the OP's code that each item in the array is an object (hence the reference to `.number`), so this won't work. – Mitya Nov 24 '19 at 17:44
1

You could do something like:

let dups = this.phoneList.filter(item =>
    this.phoneList.filter(item2 => item.number == item2.number).length > 1
);
if (dups.length) {
    this.toastrService.error('Phone already in List!');
    return true;
}

...though it suffers a little for readability.

Mitya
  • 33,629
  • 9
  • 60
  • 107
  • 1
    "*though it suffers a little for readability.*" and performance, since it's `O(n^2)` same as OP's solution. – VLAZ Nov 24 '19 at 17:51
1

You can use Array.some to check if a phone number is a duplicate, as shown below. In the loop callback, the phone number is the key of a boolean value added to the exists object. The loop stops as soon as the callback function returns true, which happens when a key/value corresponding to the loop item is found in exists.

checkDuplicate(): boolean {
  let exists: { [key: number]: boolean } = {};
  return this.phoneList.some(phoneListItem => {
    if (exists[phoneListItem.number]) {
      return true;
    } else {
      exists[phoneListItem.number] = true;
      return false;
    }
  });
}

See this stackblitz for a demo.

ConnorsFan
  • 70,558
  • 13
  • 122
  • 146
1

It's not really about angular but just the JavaScript. You could just short cycle the loop on the list as faster.

Each inner loop is n-i faster (less to do/check) since we already checked those

var xObj = {
  phoneList: [{
      name: "freddy",
      number: 55512121234
    }, {
      name: "Jimmy",
      number: 55512121234
    }, {
      name: "Mommy",
      number: 55512121233
    },
    {
      name: "Tommy",
      number: 55512121244
    },
    {
      name: "Luka",
      number: 55512121222
    },
    {
      name: "Penny",
      number: 55512121255
    },
    {
      name: "Sammy",
      number: 55512121266
    },
    {
      name: "Bill",
      number: 55512121244
    }
  ],
  phoneList2: [{
      name: "freddy",
      number: 55512121234
    }, {
      name: "Jimmy",
      number: 55512121235
    }, {
      name: "Mommy",
      number: 55512121233
    },
    {
      name: "Tommy",
      number: 55512121244
    },
    {
      name: "Luka",
      number: 55512121222
    },
    {
      name: "Penny",
      number: 55512121259
    },
    {
      name: "Sammy",
      number: 55512121266
    },
    {
      name: "Bill",
      number: 55512121247
    }
  ],
  toastrService: {
    error: function(message) {
      console.log(message);
    }
  },
  checkDuplicate: function() {
    let hasDupe = false
    for (let i = 0; i < this.phoneList.length; i++) {
      for (let j = i + 1; j < this.phoneList.length; j++) {
        if (this.phoneList[i].number === this.phoneList[j].number) {
          hasDupe = true;
          break;
        }
      }
      if (hasDupe) break;
    }
    if (hasDupe) this.toastrService.error('Phone already in List!');
    return hasDupe;
  },
  checkDuplicate2: function() {
    let hasDupe = false
    for (let i = 0; i < this.phoneList2.length; i++) {
      for (let j = i + 1; j < this.phoneList2.length; j++) {
        if (this.phoneList2[i].number === this.phoneList2[j].number) {
          hasDupe = true;
          break;
        }
      }
      if (hasDupe) break;
    }
    if (hasDupe) this.toastrService.error('Phone already in List!');
    return hasDupe;
  }
};
let cdup = xObj.checkDuplicate();
let cdup2 = xObj.checkDuplicate2();

console.log("dup:", cdup, cdup2);
Mark Schultheiss
  • 32,614
  • 12
  • 69
  • 100
  • This is *still* `O(n^2)`. Yes, it's faster than going all through the array but it doesn't change the time complexity. – VLAZ Nov 24 '19 at 19:51
  • It also depends on the array since if the first and second match or if second and third match, it is done at that point with true. This also works on older browsers. – Mark Schultheiss Nov 24 '19 at 19:53
  • Older browsers don't have Set but you can still use a plain object and you can use one to go through the array and track values. You still get constant time retrieval and the whole complexity of the algorithm is `O(n)`. In fact, with an object you will get the benefit of having the same best-case complexity *and* a much better average and worse-case complexity. – VLAZ Nov 24 '19 at 20:04