5
$\begingroup$

Is there a function that extends Tally in the following way (essentially need an answer to this guy's homework question): the a b c are complicated expressions:

myList = {{a, 2}, {b, 2}, {c, 5}, {b, -1}, {a, 3}, {b, 1}, {c, 1}, {a, Pi}}

Combine the list to obtain:

{{a, 5 + Pi}, {b, 2}, {c, 6}}

It adds up the second elements for the common first element.

If the data values were all positive integers, then I could use

Map[Flatten, Tally[myList /. {x_, n_} :> Sequence @@ ConstantArray[{x}, n]]]

But with negative or irrational numbers (or symbols), it doesn't work. Any ideas?

$\endgroup$
  • $\begingroup$ @Xavier it's not quite a duplicate question of 4332 $\endgroup$ – QuantumDot Jan 4 '16 at 0:18
  • $\begingroup$ I rather agree with @Xavier -- I answered just this question here (60205) as the two are very similar, therefore I favor closing this as a duplicate of (4332) anyway. $\endgroup$ – Mr.Wizard Jan 14 '16 at 22:43
  • 1
    $\begingroup$ @Mr.Wizard Your answer makes it clear my question is a duplicate of 4332. The OP of that question used a data set with all numbers, and as a result it wasn't immediately clear to me that he wanted to do the same thing I wanted to do. $\endgroup$ – QuantumDot Jan 14 '16 at 23:18
  • $\begingroup$ Okay, marked as such. There's about a dozen duplicates now, many with subtle variations, but at least this way we keep things centralized around that "hub" of a question. $\endgroup$ – Mr.Wizard Jan 15 '16 at 0:17
6
$\begingroup$
{#[[1, 1]], Total[#[[All, 2]]]} & /@ GatherBy[myList, First]

(*  {{a, 5 + Pi}, {b, 2}, {c, 6}}  *)
$\endgroup$
  • $\begingroup$ This is the pre v10.0 method way of doing it. $\endgroup$ – QuantumDot Jan 4 '16 at 0:19
  • $\begingroup$ @QuantumDot eldo posted a V10 method a few minutes before me so I posted this. both work well. With pre V10 you do not have to convert from lists and then back to lists (given you want lists as ouput) $\endgroup$ – Mike Honeychurch Jan 4 '16 at 0:23
10
$\begingroup$
GroupBy[myList, First -> Last, Total]

enter image description here

List @@@ Normal@GroupBy[myList, First -> Last, Total]

enter image description here

$\endgroup$
  • $\begingroup$ Or to get a list of sublists: KeyValueMap[List, GroupBy[myList, First -> Last, Total]]. $\endgroup$ – user31159 Jan 3 '16 at 13:21
  • $\begingroup$ Unfortunately, I'm still on V 10 :) $\endgroup$ – eldo Jan 3 '16 at 13:30
5
$\begingroup$

For completude, here is a solution with replacements rules.
Mike's and eldo's solutions are certainly better in nearly all cases :

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

or something more robust :

FixedPoint[  
Replace[#,{a___, {x_, y_}, b___, {x_, z_}, c___} :> {a, {x, y + z}, b, c}]&,  
myList]

These methods are very slow.

The first solution is less robust because the rule is applied at every level. It could be applied to subexpression which are not involved (inside the first part of each element of myList). On the contrary, Replace[] apply the rule to only level 0 (by default).

$\endgroup$

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