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
mhamberg1mhamberg1 

How to query Map instance

I have a map instance of two strings. Each record is a zip code and a text literal (for example <90210, 'IP'> or <90210, 'RP'> ). The combination of both values makes each record unique.

In my bulk load process, I have a need where I need to query against this Map object. I haven't seen where anyone has done this and I'm wondering what kind of syntax I should use.

My select statement looks like this - I need help on the where clause
Code:

Map<String, String> zips = new Map<String, String>();

// Simplified example
zips.put('90210','IP');
for(Zip_Code__c zip_code:[select Name, Sales_Rep__c from Zip_Code__c where Name in :zips]) 
{
// Some action code here
}

 What I really want to do is something like:
select Name, Sales_Rep__c from Zip_Code__c where Name = zips.key AND IP_or_RP__c = zips.value

How do I do that?
JimRaeJimRae
A map will not support the scenario you are describing.  The first field you pass in is the index and must be unique.
If you "put" a record in that looked like <90210,'RP'> and then "put" one in that was <90210,'IP'> the second would overwrite the first in the map.  So, you will need to address that differently.

Second, the way a Map works, you "put" the elements in, like you describe "zips.put('90210','IP')"

To retreive the value, you reference the index key in a get.

"zips.get('90210')" would return  a string 'IP'.

The way you have zips referenced in your SOQL query, would work for a set or a list, to do this with a map, you would use the keyset() method to return an array of the keys.

Code:
for(Zip_Code__c zip_code:[select Name, Sales_Rep__c from Zip_Code__c where Name in :zips.keyset()]) 
{ 
     // Some action code here
}

 Before you will be able to get to your last SOQL style, you will need to determine how to overcome your duplicate index issue.  I thought in your previous question, your zips map had a key that was the zipcode and a value that was the reps name.  Is there a possibility that more than one rep has the same zipcode? Is that situation you are trying to overcome?



mhamberg1mhamberg1
Yes, we have two divisions (IP and RP) so having the same zip code for a sales person in each division is the norm.

What would be a way to handle the case where I have unique rows based on two pieces of data? Should I somehow merge them together?

Is there a different way perhaps to have parallel Lists or multidimensional arrays or something of the sort?

Any ideas?
kevinbnpowerkevinbnpower
You can have a map of any type of object, including a map of arrays.

So, in the typical map declaration, you might create something like:

map<ID, Integer> myMap = new map<Id, Integer>();

BUT, you could create a map like:

map<ID, List<Opportunity>> myMultiDimensionalMap = new map<ID, List<Opportunity>>();

Then, when you use a map object's static GET() method, an array of your objects will be returned (Opportunities in the example above).

list<Opportunity> myOppList = myMap.Get(myID);




Message Edited by kevinbnpower on 12-11-2008 11:09 AM
JimRaeJimRae
Kevin is correct, that is how you would construct a multi dimensional map.  There are various samples on these boards, and in the documentation that would show you how to populate the map of arrays and how to leverage the data once you got the map populated.  I think what you are looking for is a map of a map.
If I understand your issue, you need something like this:

Zip , IP, Rep                 (90210,'IP',005000000071yG5AAI)
Zip, RP, Different Rep (90210,'RP',005000000071xB2AII)

You will need to query the first map using the zip (90210 in our example), to get a second map that has the 2 division codes and appropriate userID listed.






Message Edited by JimRae on 12-11-2008 02:40 PM
JimRaeJimRae
OK,
This was interesting, and I was curious how to make it work myself.
So, this is what I came up with.
My Zip_Code object (Zip_Code__c) contains 3 fields of interest:
Name (the Zip Code itself)
Division (The RP/IP Value)
Sales_Rep (the user id of the Sales Rep with the territory)

I updated your original trigger, from the other issue, with a nested map to get the zip code and the division.  What I don't know is how you get the division from the lead object to know which to take, that you will have to adjust yourself.

Here is a working trigger sample:

Code:
trigger LeadAssignmentTrigger on Lead (before insert) 
{    
    List<String> zips = new List<String>();
    List<Lead> leadsToUpdate = new List<Lead>();
    // Loop through Leads and create list of zip codes
    for (Lead lead : Trigger.new)
    {
        if (lead.PostalCode != NULL)
        {
            zips.add(lead.PostalCode);
            leadsToUpdate.add(lead);    
        }
    }
    system.debug('\n\nZIP Map size = '+zips.size());
 system.debug('\n\nLead Update List size = '+leadstoUpdate.size());
    //query zip code table and link zips to Sales reps
    Map<String, Map<String,ID>> zipToRep = new Map<String, Map<String,ID>>();

    for(Zip_Code__c zip_code:[select Name, Division__c, Sales_Rep__c from Zip_Code__c where Name in :zips]) { 
       system.debug('\n\nZIP NAME= '+zip_code.Name);
       if(zip_code.Name != null){
        if(zipToRep.containsKey(zip_code.Name)){
         system.debug('\n\nIN CONTAINS KEY');
         Map<String,ID> tempDiv = zipToRep.get(zip_code.Name);
         tempDiv.put(zip_code.Division__c,zip_code.Sales_Rep__c);
         zipToRep.put(zip_code.Name,tempDiv);
        }else{
         system.debug('\n\nIN NEW KEY');
         system.debug('\n\nZIP='+zip_code.Name);
         system.debug('\n\nDIV='+zip_code.Division__c);
         system.debug('\n\nREPID='+zip_code.Sales_Rep__c);
         zipToRep.put(zip_code.Name,new Map<String,ID>{zip_code.Division__c =>zip_code.Sales_Rep__c});
         system.debug('\n\nzipToRep='+zipToRep);
        }
        system.debug('\n\nZIPTOREP: '+zipToRep);
       }
    } 

    // If there is at least one match, bulk update
    if (leadsToUpdate.size() > 0)
    {
        for (Lead lead : Trigger.new)
        {
            // only update if the lead zip code exists in the Map
            if (zipToRep.containsKey(lead.PostalCode))
            {
              Map<String,ID> lOwner = zipToRep.get(lead.PostalCode);
              
              //assign the lead owner to the zip code owner
              system.debug('\n\nLEAD OWNER: '+lOwner);
              //lead.OwnerID = lOwner.get(—–);//How do you know what division, based on the lead, then uncomment
            }
        }
          
     }     
}

 

kevinbnpowerkevinbnpower
I actually would avoid that and instead code a map of lists with the zip as your lookup, especially if you want to do this the quick and dirty way. If you only have two divisions (IP/RP), then simply generate a new map with a string array to hold the values and consistently assign one to the 0 index and one to the 1 index. (you could do this with a map of a map I suppose, and just use IP/RP as your index value as a string) The downside is you'll need to test your get statements prior to execution (you'll error-out on null reference otherwise...) but you'll vast simplify your construct.

It's sort of a paynow/paylater issue.  Perhaps as a natural procrastinator I lean towards 'pay later' ;)

To answer Jim's question, you'll want to assign the reference of the inner value to map that you can use as a 'holder'.

So:
tempMap = myMap.Get(ID);
value = tempMap.Get(whatever);

Because values are automatically passed by reference in APEX (modify a copy of a contact and you modify the original...), this assignment works as the tempMap comes to represent the data in the inner map.



JimRaeJimRae
Depends on what the goal is, and the ultimate direction of this functionality. If there would eventually be 10 divisions, the parallel lookups could become cumbersome to manage.  The nested mapping will automatically handle any additional divisions being added with no code change.