4

I'm trying to compare Lead and Contact email addresses for duplicates and then list the duplicate emails on a Visualforce Page.

I'm basically querying all Lead records in the system (1239) and all Contact records (3681). I then add the emails to 2 separate List<String>, then loop through adding duplicates to a a Set<String> for duplicates.

Dupes then gets passed through to my Visualforce Page.

This works on a small set of data in sandbox but cant handle the larger volume.

Here's my code:

global class deDuper {

  global Set<String> Dupes {
    get;
    set;
  }

  global deDuper() {
    List<String> leadEmails = new List<String>();

    List<Lead> Allleads = [SELECT Email FROM Lead WHERE Email != null];

    List<String> ContactEmails = new List<String>();

    List<Contact> AllCons = [SELECT Email FROM Contact WHERE Email != null];

    for (Contact ConstoAdd : AllCons) {
      ContactEmails.Add(ConstoAdd.Email);
    }

    for (Lead LeadstoAdd : Allleads) {
      leadEmails.Add(LeadstoAdd.Email);
    }

    Dupes = new Set<String>();
    for (String L : leadEmails) {
      for (String C : ContactEmails) {
        if (L == C) {
          Dupes.Add(L);
        } else {
        }
      }
    }
  }

  global Set<String> getDupes() {
    return Dupes;
  }
}

3 Answers 3

2

You should look into using Sets instead of Lists when you loop over your query results.

Sets have a contains() method, which would allow you to remove the nested for loop when you check for duplicates.

The only changes you need to make are:

  • declare your two (email) List<String> lines to be Set<String>
  • change your initialization from = new List<String>() to = new Set<String>()

Your duplicate checking loop then becomes

for(String L : leadEmails){
    if(ContactEmails.contains(L)){
        Dupes.add(L);
    }
}
2
  • Thanks Derek, this worked perfectly for my production data, I made the changes quickly and easily. Thanks very much for your help.
    – DanC
    Commented Oct 21, 2015 at 8:37
  • Glad it worked out for you. My answer only required a few changes, but sfdcfox's answer is much more elegant, and almost certainly would run faster as well. I'd consider accepting his answer instead of mine.
    – Derek F
    Commented Oct 21, 2015 at 12:51
11

There's a better way to get duplicates. In fact, what you're looking for is a function called "retainAll" that you can use from a set. It works like this:

Set<String> contactEmails = new Set<String>(), leadEmails = new Set<String>(), dupeEmails;
for(Contact record:[SELECT Email FROM Contact WHERE Email != NULL]) {
    contactEmails.add(record.Email);
}
for(Lead record:[SELECT Email FROM Lead WHERE Email != NULL]) {
    leadEmails.add(record.Email);
}
dupEmails = leadEmails.clone();
dupEmails.retainAll(contactEmails);

This is a very fast function that works in nearly linear time.

3
  • I've got something out of this. Not heard of clone or retainAll methods before. This definitely looks like it'd be a quick way of doing things.
    – Dan Jones
    Commented Oct 20, 2015 at 16:51
  • 2
    @Poet Also read up on "removeAll", which would have the opposite effect; it'd remove all the dupes and keep just the unique values.
    – sfdcfox
    Commented Oct 20, 2015 at 16:52
  • Thanks for your help sfdcfox and Poet - I'll give both these methods a go.
    – DanC
    Commented Oct 21, 2015 at 8:39
1

The reason you're getting an Apex timeout error is largely because you're using nested for loops as you've correctly identified. You're effectively looping through the list you've already gotten to (potentially) find an e-mail in another loop and continuously repeating this.

A better way might be to do something like this:

Set<String> mySet = new Set<String>(); 

for (Lead l : [SELECT Email FROM Lead WHERE Email != null]) { 
  mySet.add(l.Email); 
}

for (Contact c : [SELECT Email FROM Contact WHERE Email != null]) {
  mySet.contains(c.Email); //returns true or false
}

Effectively, as you can probably tell you're adding the Lead to a set and then as you're getting data from the Contact object, using the contains method to check whether something already exists in there. This is a boolean so then you can decide what you would want to do with these duplications.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .