function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Chitral ChaddaChitral Chadda 

Unique value of picklist in contacts related to single account

I have a picklist Priority__c on contact whith values 1 ,2,3...upto 10
i want that for single account , there shud be no duplicate picklist  value of priority in contacts (limit 10 )
if user tries to enter a duplicate value of priority , then display error.

I am stuck at this map concept.
since in MAP there shoould be single id of account  and list of contacts storing priority
i m quering it from account list but something is wrong.
trigger UniquePriority on Contact (before insert,before update) {
 set<id> accId = new set<id>();
     for(contact c: trigger.new)
     {
      if (c.accountid!=null)
       {
        accId.add(c.accountId);
      }
     }
   
 //Here i used  account list to maintain unique id (single account id)  with limit of 10 contacts   
    list<account> acc = [select id ,(select Priority__c from contacts where Priority__c!=null) from account where id In: accid ];
    map<id,list<contact>> accMap = new map<id,list<contact>>();

//[Error] Error: Compile Error: Loop variable must be of type SOBJECT:Account at line 14 column 17
//But if i change this to for(account a : acc.contacts)  i will have to chasnge map to map<id,list<account>> and everything goes wrong then.
    for(contact a : acc)
     {
      if(!accMap.containskey(a.accountid))
       {
       accMap.put(a.accountid,new List<contact>());
       }
      else
      {
      accMap.get(a.accountid).add(a);
      }
     }
     
  for(contact c: trigger.new)
  {   
        if(accMap.containsKey(c.accountid) && c.Priority__C!=null)
         {
               list<contact> dupPriority =accMap.get(c.Accountid);
               {
                    for( contact dp: dupPriority)
                    {
                      if( c.Priority__c == dp.Priority__c)
                     {
                     c.addError('Same priority exists');
                     }
                    } 
                }
          }
   }      
       
       
     
}
Best Answer chosen by Chitral Chadda
pconpcon
Below is my code with annotations as to what everything is doing.  There is no reason to query the account instead of querying the contacts directly.  Doing it this way and adding the limit will only cause confusion later and could end up with invalid information coming back from the query.  It is better to just get all the results back.
 
trigger UniquePriority on Contact (before insert, before update) {
    Set<Id> accountIds = new Set<Id>();

    // Iterate over all of the contacts we have inserted or updated
    for (Contact contact: Trigger.new) {
        // Add all the account Ids to the account Id set
        accountIds.add(c.accountId);
    }

    // Remove null if it happens to be in the set
    accountIds.remove(null);

    // We'll use this map to be Account Id => Set of values we've seen
    Map<Id, Set<Integer>> prioMap = new Map<Id, Set<Integer>>();

    // Build up the map with empty sets to keep from getting Null Pointers
    for (Id id: accountIds) {
        prioMap.put(id, new Set<Integer>());
    }

    // Query all of our existing contacts
    for (Contact contact: [
        select Priority__c
        from Contacts
        where Priority__c != null and
            AccountId in :accountIds
    ]) {
        // Add their values to the map.  First we get the set from the map
        //    then add the Priority__c field to the set.
        prioMap.get(contact.AccountId).add(contact.Priority__c);
    }  

    for (Contact contact: Trigger.new) {
        // Verify that we don't alreay have the contact's priority
        if (
            prioMap.containsKey(contact.AccountId) && // Verify that the account is in the map
            contact.Priority__c != null && // Verify that the Contact's Priority is set
            prioMap.get(contact.AccountId).contains(contact.Priority__c) // See if we've already got it
        ) { 
            // This will only get added if the set returned from prioMap contains our
            //    contact's Priority__c
            contact.addError('Same priority exists');
        }  
    }       
}

Please let me know if there is something that is unclear to you about this trigger.  If this does not meet your needs, please let me know what about it does not meet your needs.

NOTE: When you add code, please use the "Add a code sample" button (the icon is <>) to increase readability

All Answers

BalajiRanganathanBalajiRanganathan
You can try below code 
trigger UniquePriority on Contact (before insert,before update) {

  Set<id> accIdSet = new Set<id>();
  Set<String> prioritySet = new Set<String>();

  for(contact c1: trigger.new) {
    if (c1.accountid != null) {
      accIdSet.add(c1.accountId);
    }
  }

  for (Contact c2 : [Select Priority__c, AccountId from Contact where AccountId in : accIdSet]) {
    prioritySet.add(c2.accountId + '_' + c2.Priority__c));
  } 

  for(contact c3: trigger.new) { 
    if (c3.accountid != null) {
      if (!prioritySet.add(c3.accountId + '_' + c3.Priority__c)) { 
        c3.addError('Same priority exists');
      }
    }
  }

}
pconpcon
The problem is that your query is on Account but your loop is on Contact.  Below is how I would program the trigger
 
trigger UniquePriority on Contact (before insert, before update) {
    Set<Id> accountIds = new Set<Id>();

    for (Contact contact: Trigger.new) {
        if (contact.AccountId != null) {
            accountIds.add(c.accountId);
        }
    }   
        
    Map<Id, Set<Integer>> prioMap = new Map<Id, Set<Integer>>();

    for (Id id: accountIds) {
        prioMap.put(id, new List<Contact>());
    }
    
    for (Contact contact: [
        select Priority__c
        from Contacts
        where Priority__c != null and
            AccountId in :accountIds
    ]) {
        prioMap.get(contact.AccountId).add(contact.Priority__c);
    }  
        
    for (Contact contact: Trigger.new) {
        if (
            prioMap.containsKey(contact.AccountId) &&
            contact.Priority__c != null &&
            prioMap.get(contact.AccountId).contains(contact.Priority__c)
        ) { 
            contact.addError('Same priority exists');
        }  
    }       
}

This method will save you time instead of doing the comparison in the loop.  If Priority__c is a Text field instead of an Integer, just change line 10 to be the following insted.
Map<Id, Set<String>> prioMap = new Map<Id, Set<String>>();

NOTE: This code has not been tested and may contain typographical or logical errors.
Chitral ChaddaChitral Chadda
@Balaji thanks
@pcon thnx buddy 

1 thing 
if my list is on account ,cant i make for loop like //for(contact c: acc .contacts) ??
************
list<account> acc = [select id ,(select Priority__c from contacts where Priority__c!=null) from account where id In: accid ];
map<id,list<contact>> accMap = new map<id,list<contact>>();

for(contact c: acc .contacts)
 {
      if(!accMap.containskey(a.accountid))
       {
       accMap.put(a.accountid,new List<contact>());
       }
      else
      {
      accMap.get(a.accountid).add(a);
      }
  }
i tried bt i got error  [Error] Error: Compile Error: Initial term of field expression must be a concrete SObject: LIST<Account> at line 17 column 21
pconpcon
There is really no reason to query on account.  The only information you are pulling is from the Contact. You might as well just query Contacts directly and use the AccountId field
Chitral ChaddaChitral Chadda
@ pcon , hello 
i made little modification to the code

trigger UniquePriority1 on Contact (before insert, before update) {
    Set<Id> accountIds = new Set<Id>();

    for (Contact contact: Trigger.new) {
        if (contact.AccountId != null) {
            accountIds.add(contact.accountId);
        }
    }   
        
    Map<Id, list<Integer>> prioMap = new Map<Id, List<Integer>>();

    for (Id id: accountIds) {
        prioMap.put(id, new List<Contact>()); // Error: Compile Error: Incompatible value type LIST<Contact> for MAP<Id,LIST<Integer>> at line 13 column 9

    }
    
    for (Contact contact: [
        select Priority__c
        from Contact
        where Priority__c != null and
            AccountId in :accountIds
    ]) {
        prioMap.get(contact.AccountId).add(contact.Priority__c);
    }  
        
    for (Contact contact: Trigger.new) {
        if (
            prioMap.containsKey(contact.AccountId) &&
            contact.Priority__c != null &&
            prioMap.get(contact.AccountId).contains(contact.Priority__c)
        ) { 
            contact.addError('Same priority exists');
        }  
    }       
}

1 more thing // can u help me in my code cz i realyy cnt figure out that little thing , my head banged !
in picklist Priority__c ( on contact) i have 10 picklist values only so generally i have to put limit 10 on contact so that it checks only for 10 contacts 
if i put limit 10 here (the code you shared)
for (Contact contact: [ select Priority__c  from Contact where Priority__c != null andAccountId in :accountIds limit 10 ])

// it will put limit on account also get 10 account 
but for single account i want it to check for 10 contacts
so as in my code i provided i used a subquery thats why

trigger UniquePriority on Contact (before insert,before update) {
 set<id> accId = new set<id>();
     for(contact c: trigger.new)
     {
      if (c.accountid!=null)
       {
        accId.add(c.accountId);
      }
     }
   
    
  
 list<account> acc = [select id ,(select Priority__c from contacts where Priority__c!=null) from account where id In: accid ];
    map<id,list<contact>> accMap = new map<id,list<contact>>();


    for(account a : acc)
     {
     if(!accMap.containskey(a.aid))
       {
       accMap.put(a.id,new List<contact>());
       }
      else
      {
      accMap.get(a.id).add(a); //Error: Compile Error: Incompatible element type SOBJECT:Account for collection of SOBJECT:Contact at line 25 column 24    : i get this error wen i save this code // cz i need to use this list <contact>  in the ****** marked area

      }
     }
     
     
     
  for(contact c: trigger.new)
  {   
        if(accMap.containsKey(c.accountid) && c.Priority__C!=null)
         {
               list<contact> dupPriority =accMap.get(c.Accountid); // ******  here i need to get use that list
               {
                    for( contact dp: dupPriority)
                    {
                      if( c.Priority__c == dp.Priority__c)
                     {
                     c.addError('Same priority exists');
                     }
                    } 
                }
          }
   }      
 }      
pconpcon
Why are you trying to make these modifications?  What is your goal?  Based on your initial goal of restricting insert / update of Contacts based on Priority__c, the code I provided should work without issue.

You cannot add the limit restrictions to the query because if you insert / update a bulk number of contacts across multiple you will not get all of the results back.

There are several reason why your code above would fail (mainly because of your variable types do not match)

NOTE: When you add code, please use the "Add a code sample" button (the icon is <>) to increase readability
Chitral ChaddaChitral Chadda
Thnk u so mcg , i made litle modification to ur code ( type change) n it worked perfect ! Clear understanding But for my basic understanding , i m not clear with this part of code trigger UniquePriority on Contact (before insert,before update) { set accId = new set(); for(contact c: trigger.new) { if (c.accountid!=null) { accId.add(c.accountId); } } List myList = new List(); Map> accMap = new Map>(); for (Account acc : [select id ,(select Priority__c from contacts where Priority__c!=null limit 10 ) from account where id In: accid ]) { accMap.put(acc.ID,acc.Contacts); for(Contact c : acc.Contacts) { myList.add(c.Priority__c); } } for(contact ct: trigger.new) { if(ct.Priority__c!=null && accMap.containsKey(ct.AccountId) && accMap.get(ct.AccountId).contains(ct.Priority__c)) //error llist.contains(string) // here i check if for tgat account id it contains priority tyen give error bt it dosent work { ct.addError('Same priority exists'); } } }
pconpcon
Below is my code with annotations as to what everything is doing.  There is no reason to query the account instead of querying the contacts directly.  Doing it this way and adding the limit will only cause confusion later and could end up with invalid information coming back from the query.  It is better to just get all the results back.
 
trigger UniquePriority on Contact (before insert, before update) {
    Set<Id> accountIds = new Set<Id>();

    // Iterate over all of the contacts we have inserted or updated
    for (Contact contact: Trigger.new) {
        // Add all the account Ids to the account Id set
        accountIds.add(c.accountId);
    }

    // Remove null if it happens to be in the set
    accountIds.remove(null);

    // We'll use this map to be Account Id => Set of values we've seen
    Map<Id, Set<Integer>> prioMap = new Map<Id, Set<Integer>>();

    // Build up the map with empty sets to keep from getting Null Pointers
    for (Id id: accountIds) {
        prioMap.put(id, new Set<Integer>());
    }

    // Query all of our existing contacts
    for (Contact contact: [
        select Priority__c
        from Contacts
        where Priority__c != null and
            AccountId in :accountIds
    ]) {
        // Add their values to the map.  First we get the set from the map
        //    then add the Priority__c field to the set.
        prioMap.get(contact.AccountId).add(contact.Priority__c);
    }  

    for (Contact contact: Trigger.new) {
        // Verify that we don't alreay have the contact's priority
        if (
            prioMap.containsKey(contact.AccountId) && // Verify that the account is in the map
            contact.Priority__c != null && // Verify that the Contact's Priority is set
            prioMap.get(contact.AccountId).contains(contact.Priority__c) // See if we've already got it
        ) { 
            // This will only get added if the set returned from prioMap contains our
            //    contact's Priority__c
            contact.addError('Same priority exists');
        }  
    }       
}

Please let me know if there is something that is unclear to you about this trigger.  If this does not meet your needs, please let me know what about it does not meet your needs.

NOTE: When you add code, please use the "Add a code sample" button (the icon is <>) to increase readability
This was selected as the best answer
Chitral ChaddaChitral Chadda
trigger UniquePriorityUniqueUnique on Contact (before insert,before update) {
set<id> accId = new set<id>();
     for(contact c: trigger.new)
     {
      if (c.accountid!=null)
       {
        accId.add(c.accountId);
      }
     }
  
     List<String> myList = new List<String>();
      Map<String,List<Contact>> accMap = new Map<String,List<Contact>>();
            for (Account acc : [select id ,(select Priority__c from contacts where Priority__c!=null limit 10 ) from account where id In: accid  ])
            {
       
                accMap.put(acc.ID,acc.Contacts);
       
                for(Contact c : acc.Contacts)
                {
                    myList.add(c.Priority__c);
           
                }
               
            }
    
  for(contact ct: trigger.new)
   {  
        if(ct.Priority__c!=null &&  accMap.containsKey(ct.AccountId) &&  accMap.get(ct.AccountId).contains(ct.Priority__c))   //Error	***Error: Compile Error: Method does not exist or incorrect signature: [LIST<Contact>].contains(String) at line 28 column 99	

         {
               ct.addError('Same priority exists');
         }
   }     
       
       
     
}

Thnku for u spontaneous and helpful reply .
and yup here is the formatted code previously.

for my basic understanding , i m not clear with this part of code  ..wen i save this code it gives error 

although my reruirement is solved but for understanding level if u can explain why id this issue .
 

pconpcon
This is not working because you're accMap is of type List<Contact> not Set<String>.  Based on your code (which I still say is not the best way to do this) you need to change it to the following:
 
trigger UniquePriorityUniqueUnique on Contact (before insert,before update) {
    set<id> accId = new set<id>();
    for (contact c: trigger.new) {
        if (c.accountid != null) {
            accId.add(c.accountId);
        }
    }

    Map<String, Set<String>> accMap = new Map<String, Set<String>>();
    for (Account acc: [
        select Id ,(
            select Priority__c
            from contacts
            where Priority__c != null
            limit 10
        )
        from account
        where id In: accid
    ]) {
        accMap.put(acc.Id, new Set<String>());

        for (Contact c: acc.Contacts) {
            accMap.get(acc.Id).add(c.Priority__c);
        }
    }   

    for (contact ct: trigger.new) {  
        if (
            ct.Priority__c!=null &&
            accMap.containsKey(ct.AccountId) &&
            accMap.get(ct.AccountId).contains(ct.Priority__c)
        ) {
            ct.addError('Same priority exists');
        }
    }
}

In your code you were doing a contains on a List which does not have the method contains.  Also, you were adding prorities to a List that you were not doing anything with.  Finally, you were trying to do a contains on a list of Contacts and checking to see if it contains a Priority (which is a string).

Is there any reason why the code I have supplied to you serveral times does not meet your needs?  It seems like you could have taken that code and already be done with this issue.
Chitral ChaddaChitral Chadda

GOT IT !
 THANKS MAN !!!!

* KUDOS *