# How to sum over duplicates?

I have a list of the form:

{{1, 0, 1}, {2, 1, 2}, {1, 0, 3}, {2, 4, 2}, {2, 1, -1}, {1, 0 ,0}}


that I would like to sort such that I calculate the sum over the third column whenever the first two columns are equal. I.e. in this case the output would be

{{1, 0, 1 + 3 + 0}, {2, 1, 2 + (-1)}, {2, 4, 2}}


or

 {{1, 0, 4},{2, 1, 1},{2, 4, 2}}


Is there an elegant way to do this?

-
Welcome to Mathematica.SE! I suggest the following: 1) As you receive help, try to give it too, by answering questions in your area of expertise. 2) Read the faq! 3) When you see good questions and answers, vote them up by clicking the gray triangles, because the credibility of the system is based on the reputation gained by users sharing their knowledge. Also, please remember to accept the answer, if any, that solves your problem, by clicking the checkmark sign! – Mike Honeychurch Dec 18 '12 at 0:36
@rm-rf Looking at your first link the principle is the same. However gathering based on both first and second columns, as opposed to just the first, is probably not obvious to a new user. Keeping the first 2 columns together also means that you have to wrap Flatten around the mapped function. – Mike Honeychurch Dec 18 '12 at 0:43

Flatten[{#[[1, {1, 2}]], Total[#[[All, 3]]]}] & /@ GatherBy[yourList, #[[{1, 2}]] &]

(*  {{1, 0, 4}, {2, 1, 1}, {2, 4, 2}} *)


How this works:

GatherBy[yourList, #[[{1, 2}]] &]


this gathers elements of your list based on matching the first and second elements in each sub-list (#[[{1, 2}]] &)

Once common elements are gathered (collected) you can total/sum all the third elements by mapping them onto Total

-
Perfect! I was unaware of the GatherBy function and, yes, using it with more than one column was not obvious to this new user. – Paul Dec 18 '12 at 0:48
 lists = {{1, 0, 1}, {2, 1, 2}, {1, 0, 3}, {2, 4, 2}, {2, 1, -1}, {1, 0, 0}}


Reap/Sow:

 Reap[Sow[#[[3]], {#[[{1, 2}]]}] & /@ lists, _,  Append[#1, Total@#2] &][[2]]


ReplaceRepeated:

 lists //. {a___, {x_, y_, z_}, b___, {x_, y_, w_}, c___} :> {a, {x, y, z + w}, b, c}


Cases + DeleteDuplicates:

 DeleteDuplicates@Cases[lists, {w : PatternSequence[_, _], _} :>
{w, Total@(Last /@ Cases[lists, {w, _}])}]


all give

 (* {{1, 0, 4}, {2, 1, 1}, {2, 4, 2}} *)

-
+1 for the ReplaceRepeated. – image_doctor Dec 18 '12 at 10:25
Total@#/PadLeft[{1}, 3, Length@#] & /@ GatherBy[list, #[[{1, 2}]] &]


This approach is basically the same as Mike's. However, instead of summing only the last element it sums all the elements. To correct for the extra summing PadLeft[{1}, 3, Length@#] is used to divide by the number of times it was added where the {1} is included to keep the last element the same. The approach can be generalized to lists of length $n$ by replacing PadLeft[{1}, 3, Length@#] with PadLeft[{1}, Length@#[[1]], Length@#]

-
You might give a bit of explanation as to what your code is doing and why it works. – Brett Champion Dec 18 '12 at 4:57

Since GatherBy and Sow/Reap are already shown I'll drop back to "first principles" as it were and use more basic language.

dat = {{1, 0, 1}, {2, 1, 2}, {1, 0, 3}, {2, 4, 2}, {2, 1, -1}, {1, 0, 0}};

f[dat_] :=
Module[{key},
key[__] = 0;
key[#, #2] += #3 & @@@ dat;
Most @ DownValues @ key /. (_@_@x__ :> y_) :> {x, y}
]

f @ dat

{{1, 0, 4}, {2, 1, 1}, {2, 4, 2}}

-

GroupBy, introduced in Mathemtaica 10, is applicable here and cleaner than GatherBy:

in = {{1, 0, 1}, {2, 1, 2}, {1, 0, 3}, {2, 4, 2}, {2, 1, -1}, {1, 0, 0}};

Append @@@ Normal @ GroupBy[in, Most -> Last, Tr]

{{1, 0, 4}, {2, 1, 1}, {2, 4, 2}}