1

I'm comparing two list given in the list to see if the item.name exists in both for loop. If not then add into a list to show the error for the missing names.

set<id> setAssetIds = new set<id>();
list<Asset_Store__c>  assets = [select id,name......];
for(Asset_Store__c item : assets )
{
    setAssetIds.add(item.id); 
} 

list<Meter__c>  meters = [select id,name......where id in : setAssetIds];

//now comparing the two lists
for(Asset_Store__c a : assets)
{
   for(Meter__c m : meters)
   {
     if(a.Name != m.Name)
     {
       //not matched...
       break;
     }

   }
}

the problem with the above approach is that, its not finding the correct name.

4

Perhaps you are looking for something like Set.contains?

Set<String> meterNames = new Set<String>();
for (Meter__c meter : [/*query*/]) meterNames.add(meter.Name);

for (Asset_Store__c asset : assets)
{
    if (!meterNames.contains(assetName))
    {
        // logic here
    }
}

You can also try the removeAll method with two sets.

Set<String> assetNames; // populate as above
Set<String> meterNames; // populate as above

Set<String> assetsWithNoMeter = assetNames.clone();
assetsWithNoMeter.removeAll(meterNames);

Set<String> metersWithNoAsset = meterNames.clone();
metersWithNoAsset.removeAll(assetNames);
  • Thanks Adrian, there is one thing that is bothering me that its not looping all the names, for an example: in meterNames I have 12 names and in the loop its only have 11 names... so for testing purpose i have data that does not match all 12 names so based on the condition in the above I should have one not matching result but its not., i have tried with trim() thinking that it must have some white space but still the same – Nick Kahn Aug 22 '16 at 14:19
  • 1
    @Nick If you want to find which names are missing, try using removeAll. If you only care about one of the sets not containing values from the other, you can drop the cloning bit. – Adrian Larson Aug 22 '16 at 14:23
  • 1
    As time goes on, I find myself liking solutions that use set operations more and more. – Derek F Aug 22 '16 at 14:33
  • I have used the technic removeAll after cloning and it works but I'm not sure if this the best approach to my problem. Is there any other way then cloning and then removeAll? I'm after finding out which Name is missing out of two lists – Nick Kahn Aug 22 '16 at 14:55
2

I highly recommend using Adrian's solution using removeAll(), but that aside, I think the issue with your posted code is that you're breaking out of the inner loop on the first non-match.

I don't think there's any situation where that logic alone would produce the result you expect (except for when both lists are exactly the same size, and have exactly the same order).

Consider the following collections: {a,b,c} and {d,c,b}

The difference between the two collections, which is what you're looking for, is {a, d}

The very first comparison that you make in your inner loop would be a != d. That alone doesn't give you enough information to determine if d exists elsewhere in the first collection, or if a exists elsewhere in the second collection.

The issue gets worse when you consider the next match that your logic would test, b != d. By looking at the two collections ourselves, we can see that b exists in both collections, but your code hasn't yet encountered that value in the second collection.

Ordering the two collections would help a little, but that alone wouldn't fix your issues. To completely solve your problem, you would also need to know at which point it becomes impossible to find the target value in the second set.

You'd also need to track the largest value in the first collection (i.e. the last iteration of the outer loop), and add all elements from the second collection that are greater than that. Without that, you'd miss some elements if your second collection has more objects than your first collection.

Without using the Set collection methods, you can't really do much better than O(n^2). The resulting code might look like this:

set<id> setAssetIds = new set<id>();
List<String> unmatchedNames = new List<String>();
list<Asset_Store__c>  assets = [select id,name...... ORDER BY Name ASC];
for(Asset_Store__c item : assets )
{
    setAssetIds.add(item.id); 
} 

list<Meter__c>  meters = [select id,name......where id in : setAssetIds ORDER BY Name ASC];

// Store the largest sorted value of the Asset_Store__c records so we know when we're in 
//  the last iteration of the outer loop
String assetLastName = assets[assets.size()-1];

//now comparing the two lists
Boolean matched;
for(Asset_Store__c a : assets)
{
   matched = false;
   for(Meter__c m : meters)
   {     
     if(a.Name == m.Name)
     {
       // Once we know that we have a match, we can move on to the next
       // iteration of the outer loop
       matched = true;
       break;
     }
     else if(a.Name < m.name)
     {
         if(a.Name == assetLastName)
         {
           // We're in the last iteration of the outer loop, and there's
           //   no possibility that any other name in the meters list can match.
           // These names must be added to the difference list.
           unmatchedNames.add(m.name);
         }
         else
         {
           // It is impossible to match the asset name further in the meter list at this point.
           // This should also take care of the case where a.name is sorted
           //   lower than any name in meters.
           // Break to save some cycles
           break;
         }
     }
   }

   // We cannot add names from the asset list to our unmatched list before
   //   this point.
   // Doing so would mean we left some possible names untested
   unmatchedNames.add(a.name);
}

Now that I'm done rambling, DON'T USE THIS CODE. Seriously, use Set.removeAll(), or at the very least Set.contains().

  • 1+Thanks @Derek F I understand that I just wanted to see if there is any other solution but as you have demonstrate in your sample code I'm better off using the set.removeAll() then looping :) – Nick Kahn Aug 22 '16 at 18:40
1

Using the methods defined in 'World's Greatest Util Methods', you can use Adrian's suggestion and reduce this to a "no loops, no if statements" code block

Asset_Store__c[] assetStores = [select id,name......from Asset_Store__c];

set<String> assetNames = new set<String>(Util.valListFromField(assetStores,Asset_Store__c.Name));

set<String> meterNames = (set<String>) Util.valListFromField(
    set<String>.class, 
    [select id,name......from Meter__c where Asset_Store__c = :assetStores],
    Meter__c.Name));

set<String> assetsWithoutMeters = assetNames.removeAll(meterNames);
  • +1 Thanks for posting I wasn't aware of the World's Greatest Util Methods – Nick Kahn Aug 22 '16 at 20:12
  • @NickKahn I learned them from sfdcfox and have been dying to use them in an answer now that I have someplace to link to in SFSE – cropredy Aug 22 '16 at 20:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.