6
$\begingroup$

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]
$\endgroup$
5
  • $\begingroup$ Transpose@*KeyValueMap[List]@*Subtract @@ KeyUnion[{Rule @@@ #2, Rule @@@ #}, 0 &] &[list1, list2]? $\endgroup$
    – kglr
    Jul 27 at 17:37
  • 1
    $\begingroup$ @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. $\endgroup$ Jul 27 at 17:42
  • 1
    $\begingroup$ Little different ~ Sort@Join[list1, list2] GatherBy[%, First] Transpose /@ % $\endgroup$
    – Teabelly
    Jul 27 at 22:24
  • $\begingroup$ Note that Key is another name for Identifier and there is a nice data structure that builds upon this called Association. $\endgroup$
    – gwr
    Jul 29 at 7:10
  • $\begingroup$ 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 $\endgroup$
    – user1066
    Jul 29 at 9:19
4
$\begingroup$

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.

$\endgroup$
4
  • $\begingroup$ A bit clunky, but we can bend Merge to our will: KeyValueMap[List]@ Merge[{Rule @@@ list2, Rule @@@ list1}, If [Length@# == 2, Subtract @@ #, First@#] &] $\endgroup$ Jul 27 at 22:25
  • $\begingroup$ @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. $\endgroup$
    – andre314
    Jul 28 at 7:38
  • $\begingroup$ @bobthechemist By "clunky" you mean the code, the text, or both ? $\endgroup$
    – andre314
    Jul 28 at 7:40
  • $\begingroup$ 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. $\endgroup$ Jul 28 at 11:08
7
$\begingroup$

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.

$\endgroup$
1
  • 3
    $\begingroup$ So very much clearer indeed; remember, humans read code, too. ;-) $\endgroup$
    – gwr
    Jul 29 at 7:04

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.