# Applying functions to list elements selectively

I've got two lists that contain an element identifier and a number:

list1 = Transpose[{{"c", "a", "b"}, {2, 1, 1}}]
list2 = Transpose[{{"a", "b", "c", "e", "f"}, {2, 2, 2, 2, 2}}]
listdesired = Transpose[{{"a", "b", "c", "e", "f"}, {1, 1, 0, 2, 2}}]


I'd like to know an efficient method to produce listdesired from list1 and list2. In this case, the desired output is found by subtracting the integer in list1 from the item in list2 that has the same identifier ('a', 'b', 'c', ...).

Update

The solution that worked for me is reproduced below since it is currently in a comment and I would like the answer to persist. The answer that has been accepted works just fine as well.

KeyValueMap[List]@*Subtract @@ KeyUnion[{Rule @@@ #2, Rule @@@ #}, 0 &] &[list1, list2]

• Transpose@*KeyValueMap[List]@*Subtract @@ KeyUnion[{Rule @@@ #2, Rule @@@ #}, 0 &] &[list1, list2]?
– kglr
Commented Jul 27, 2021 at 17:37
• @kglr no Transpose composition since I want the pairs, but yep! And thanks for using Rule @@@ list as I've been banging my head trying to remember that for some time now. Commented Jul 27, 2021 at 17:42
• Little different ~ Sort@Join[list1, list2] GatherBy[%, First] Transpose /@ % Commented Jul 27, 2021 at 22:24
• Note that Key is another name for Identifier and there is a nice data structure that builds upon this called Association.
– gwr
Commented Jul 29, 2021 at 7:10
• Also clunky: Join[list1,list2]//GatherBy[#,First]&//Map@(PadLeft[#,{2,2}]&)//Map@({#[[2,1]], #[[2,2]] - #[[1,2]]}&)//Sort Alternatively Join[list1,list2]//GatherBy[#,First]&//Map@({#[[2,1]], #[[2,2]] - #[[1,2]]}&@*(PadLeft[#,{2,2}]&))//Sort Commented Jul 29, 2021 at 9:19

In the case of a subtraction, you can do :

list1 = Transpose[{{"c", "a", "b"}, {2, 1, 1}}];
list2 = Transpose[{{"a", "b", "c", "e", "f"}, {2, 2, 2, 2, 2}}];

Merge[{Rule @@@ list2, (#1 -> -#2) & @@@ list1}, Total] //KeyValueMap[List]


{{"a", 1}, {"b", 1}, {"c", 0}, {"e", 2}, {"f", 2}}

Note that the second argument of Merge,Total, can' t do itself
the subtraction, because it doesn' t know where a unique data is
coming from (list1 or list2 ?)

More precisely, it depends on what you expect if there is an identifier in list1 that is not in list2.

• A bit clunky, but we can bend Merge to our will: KeyValueMap[List]@ Merge[{Rule @@@ list2, Rule @@@ list1}, If [Length@# == 2, Subtract @@ #, First@#] &] Commented Jul 27, 2021 at 22:25
• @bobthechemist If list1 contains an identifier that is not in in list2, for exemple list1={{"a",1},{"b",8}} and list2={{"a",1}}, your approach gives {{"a",0},{"b" , + 8}}. It is rather misleading. Commented Jul 28, 2021 at 7:38
• @bobthechemist By "clunky" you mean the code, the text, or both ? Commented Jul 28, 2021 at 7:40
• by clunky I mean it's possible to design a 2nd argument for Merge that performs the desired operation, but as you mention, it would take some work. In my actual application, list1 only contains identifiers from list2, which is why I hadn't thought of that problem. Commented Jul 28, 2021 at 11:08

Use Associations:

a = AssociationThread[{"a", "b", "c", "e", "f"}, {2, 2, 2, 2, 2}];
b = AssociationThread[{"c", "a", "b"}, {2, 1, 1}];
Merge[{a, -b}, Total]


<|"a" -> 2, "b" -> 2, "c" -> 2, "e" -> 2, "f" -> 2|>

<|"c" -> 2, "a" -> 1, "b" -> 1|>

<|"a" -> 1, "b" -> 1, "c" -> 0, "e" -> 2, "f" -> 2|>

Oh, now I see that this is basically what andre314 proposed (minus some massaging). But this actually shows that Association might provide a better representation of your data as your data are key-values lists.

• So very much clearer indeed; remember, humans read code, too. ;-)
– gwr
Commented Jul 29, 2021 at 7:04