Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

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?

share|improve this question
    
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
add comment

4 Answers

up vote 10 down vote accepted
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

share|improve this answer
    
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
add comment
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@#]

share|improve this answer
1  
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
add comment
 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}} *)
share|improve this answer
    
+1 for the ReplaceRepeated. –  image_doctor Dec 18 '12 at 10:25
add comment

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}}
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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