user630209
user630209

Reputation: 1207

How to avoid adding duplicate object in a list

This is my model class

export class PersonModel {

    persCode:number;
    name:string;
    contactNumber:number;
    remarks:string;
  }

I'm having a list of objects of PersonModel

   affectedPersons: PersonModel[] = [];

How can I check whether an object is present in this list. So that I can avoid adding duplicate values.

addVisitor() {
        let visitorVal : PersonModel ;
        visitorVal = this.affectedPersons.filter(x => x.name == this.visitorName && x.contactNumber== this.visitorMob && x.remarks==this.visitorRemark, x => console.log("x "+ JSON.stringify(x)))
            .map(x => x)[0];

        }

This is not working. How can I achieve this

Upvotes: 0

Views: 1091

Answers (1)

user663031
user663031

Reputation:

Define the concept of "equal" in the form of a JS function. That's what they are for.

function objectsAreEqual(obj1, obj2) {
  return obj1.name === obj2.visitorName &&
    obj1.contactNumber === obj2.visitorMob &&
    obj1.remarks === objs.visitorRemark;
}

Now you can find if an object is in an array of objects with

const visitorAlreadyInArray = arr.some(obj => objectsAreEqual(obj, newObj))

If you want to find the actual matching object, then

const visitorVal = arr.find(obj => objectsAreEqual(obj, newObj))

filter can work in this case, but it's not the best solution. Logically, filter is designed to narrow down a list of things. That's not what you're trying to do. You're trying to find a thing. That's what find is for. It has the advantage that it will stop once it finds the thing it is looking for. In contract, filter will continue running on all elements of the array, even after it's already found the first match.

However, this is not really an ideal solution. You've hard-wired the names of properties of your objects into the solution. That means that every time you change or add a property, you'll have to change this part of the code. That's not a good thing. We want to write a better version, which works no matter what the properties are.

To do that, you'll first need to switch to consistent naming of properties. Instead of using one name for the properties of the objects in your array (name) and another different name for the properties of the object you are trying to match (visitorName), use the same name for both (name).

Now we can write a function that compares any two objects to make sure all their properties are the same. This is an old problem. You have to decide if you want to compare the two objects in nested fashion, if they contain subobjects. Here is a thread on this topic. If you want to just do a "shallow" comparison, then you can do something like:

function compareObjects(obj1, obj2) {
  const obj1Keys = Object.keys(obj1);
  const obj2Keys = Object.keyw(obj2);

  return obj1Keys.length === obj2Keys.length &&
    obj1Keys.every(key => obj1[key] === obj2[key]);
}

Now I can use this to find the matching object in the array by saying

arr.find(obj => compareObjects(obj, newObj))

This will check that all the properties are the same. If you want instead to check if some subset of the properties are the change, you'll have to tweak this in some way, by passing in the list of properties you want to check.

Note that none of this has anything to do with TypeScript, or Angular. It's pure JavaScript. However, using TypeScript, you could write the compareObjects function in a way which is more type-safe by insisting that the two inputs both be objects, and of the same type:

function compareObjects<T extends object>(obj1: T, obj2: T) {
  const obj1Keys = Object.keys(obj1);
  const obj2Keys = Object.keyw(obj2);

  return obj1Keys.length === obj2Keys.length &&
    obj1Keys.every(key => obj1[key] === obj2[key]);
}

Now the compiler will complain if you try to pass in two incompatible objects to be compared.

Upvotes: 1

Related Questions