Apex collections are always passed by reference meaning that it is just a pointer to the original collection. If you want to create a new instance of a collection that can be independently modified you will want to use the clone() method on the collection. More information is available at https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_methods_system_set.htm
Does anyone have a code example of how to iterate through a list and remove any list items that match the items in another list? So, say I have a list of CDs and a list of LPs, I want to look through the list of CDs and remove any that are for the same artist and album as an item in the list of LPs. I'm thinking I'm probably going to need a map but I've never written a map before and can't seem to find any examples of them that remove things.
UPDATE: Thanks for all the input guys. I'm totally going to save this thread and use all this stuff in the future. As for the current scenario. I found a different solution. I was attempting to avoid an error message but the client actually wants to see the error message SO totally different scenario :)
I'd convert your list to sets, and use set.contains, and set.removeAll. Check out the set docs.
https://www.salesforce.com/us/developer/docs/apexcode/Content/apex_methods_system_set.htm#apex_System_Set_methods
I'd do it like this (provided I am understanding your question). it could be a set of anything, I'm just using Ids since I don't know what your data type is. You could the convert the set back into a list if you need. Or you can use the suggestion below of iterating the list, recording the index position (which might be a pain/inefficient to get since lists don't have a contains method, so you'd need to iterate and search the list for every element).
//set created from your first list, Im calling it listA (provided your list is a list of ids)
set<id> firstSet = new set<id>(listA);
//set created from your second list, which I'm calling listB (provided your list is a list of ids)
set<id> secondSet = new set<id>(listB);
//temp variable to hold the things to delete
list<id> thingsToRemove = new list<id>();
//iterate the first set
for(id thisId : firstSet)
{
//see if this thing is in the second set
if(secondSet.contains(thisId))
{
//if so, then add it to our list of things to remove
thingsToRemove.add(thisId);
}
}
//remove the things from the set
firstSet.removeAll(thingsToRemove);
Edit: Just realized that if you are actually storing albums as sObjects and can retrieve with SOQL, none of this is necessary. It would be one line:
List<Album> filteredCDs = [ Select Id, Name... etc from Album Where Id in : CDs and Id Not in : LPs ];
Given that, I bet all my assumptions were wrong, eh? Perhaps I should have asked more questions, but hey, nothing like writing a novel about code... while at work... supposedly working on work related code...
The problem with lists is you can only remove items by passing an index. Once it's removed, the indeces are reset, so it's pretty much impossible to iterate through a list and remove items while you're in the same loop.
Here's how I'd do it without maps (and by making big assumptions about your existing code):
private List<Album> filterCDs(List<Album> CDs, List<Album> LPs) {
List<Album> filteredCDs = new List<Album>();
for ( Album CD: CDs) {
for ( Album LP : LPs ) {
if ( LP.ArtistName != CD.ArtistName && LP.AlbumName != CD.AlbumName )
filteredCDs .add(CD);
}
}
return filteredCDs;
}
So this doesn't actually remove anything, it just only returns the cds that don't appear in the LP list. If your lists are large, this is crazy inefficient though, with the number of comparisons being CDs.size() * LPs.size().
Maps are not as scary as they seem, and are extremely useful! They're basically the same as a list/array, but can use an index other than an integer. What's cool about maps is you can build one with SOQL, and it gives you a kind of list where each index is the ID of the record.
Here's how I'd do it with a map (assuming albums are an sObject and not just a Class):
private List<Album> filterCDs(List<Album> CDs, List<Album> LPs) {
Map<Id, Album> LPMap = new Map<Id, Album>([ Select Id from Album where Id in : LPs ]);
/*
^ returns a kind of list where the indexes are the Ids of each record, like:
LPMap = {
'albumid0' = Album{
Id = 'albumid0'
},
'alumbid1' = Album{
Id = 'albumid1'
},...
}
*/
List<Album> filteredCDs = new List<Album>();
for ( Album CD: CDs) { // iterate through CDs
if ( !LPMap.containsKey(CD.Id) ) // if the LP map does *not* contain this CD id
filteredCDs.add(CD); // add it to the new list
}
return filteredCDs;
}
Calling LPMap.containsKey(some_album_id) is better than iterating through the entire list and physically checking if an album with the same title/artist is present. It's similar to checking whether or not LPMap.get(some_album_id) == null. And now the number of comparisons is just the size of the CDs list.
Definitely find more info about maps and how to build them manually with .put(key, value). Removing is just .remove(key).
Anyway, hope that makes some amount of sense... I'd have to know more about how you're already organizing things to know whether or not everything I've just typed is utterly useless or not!
apex - how to remove members of a list from a set - Salesforce Stack Exchange
Feed Item Detail | Salesforce Trailblazer Community
apex - RemoveAll on set of records before insert doesn't remove matching records in set of existing records - Salesforce Stack Exchange
apex - Best way to remove element from list or why they kept iterator without this option - Salesforce Stack Exchange
Are Apex Set elements case-sensitive for Strings?
What is a Set in Salesforce Apex?
What is the difference between a Set and a List in Apex?
The idiomatic way to do this in other languages without iterator semantics is to iterate through the list backwards.
For example:
public static List<Object> removeAll(List<Object> target, Object toRemove) {
for(Integer i = target.size() - 1; i >= 0; i--) {
if(target[i].equals(toRemove)) {
target.remove(i);
}
}
return target;
}
I don't know if it's a best practice, but one way to reverse your approach and make a second list of the items you want to keep, then once you're done iterating, reassign that to your primary list.
So, using your code, it would be:
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
List<Integer> listToKeep = new ArrayList<Integer>();
for (Integer num : list) {
if (num != 1){
listToKeep.add(num);
}
}
list = listToKeep;
System.out.println(list);
You can use the remove() method:
Map <String, String> mapOne = new Map <String, String>();
mapOne.put('key1', 'value1');
mapOne.put('key2', 'value2');
mapOne.put('key3', 'value3');
mapOne.put('key4', 'value4');
Map <String, String> mapTwo = new Map <String, String>();
mapTwo.put('key1', 'value1');
mapTwo.put('key2', 'value2');
for (String key : mapTwo.keySet())
{
mapOne.remove(key);
}
Now mapOne will only contain key3=>value3 and key4=>value4. This example is if the keys of the maps match.
If you want to remove elements from one map based on values from another, then instead of keySet() just use values().
Docs
A clear and efficient solution would be by using keySet() and removeAll() methods.
Explanation:
map.keyset() will return a reference to the set of keys of the respective map, enabling the interface to use set class methods. which in turn allows us to use the removeAll() method to remove all the keys from the map at once.
Example:
Map <String, String> mapOne = new Map <String, String>();
mapOne.put('key1', 'value1');
mapOne.put('key2', 'value2');
mapOne.put('key3', 'value3');
mapOne.put('key4', 'value4');
Map <String, String> mapTwo = new Map <String, String>();
mapTwo.put('key1', 'value1');
mapTwo.put('key2', 'value2');
System.debug(mapOne);
mapOne.keySet().removeAll(mapTwo.keyset()); // removing elements from map
System.debug(mapOne);

You've got the right idea, but just need to convert your lists to sets so you can make use of the apex removeAll() function.
Set<String> stringSet1 = new Set<String>();
stringSet1.addAll(stringList1);
Set<String> stringSet2 = new Set<String>();
stringSet2.addAll(stringList2);
Then you can use the remove all function (keep a copy of stringSet1 since you're modifying it and want to use the original to remove from string set 2)
Set<String> originalStringSet1 = stringSet1.clone();
stringSet1.removeAll(stringSet2);
stringSet2.removeAll(originalStringSet1);
After you've done that you can iterate over your string list and build the string back up with all the words that weren't common between the strings.
You can rewrite your Strings:
Iterate through words and if you have different words just add them to the end of new strings
// inside loop
if (!word1.equals(word2)) {
str1new += word1;
str2new += word2;
}
// outside of loop
s1 = str1new;
s2 = str2new;
Of course you need to add spaces between words. And how do you expect your program will work with strings with differ length?
