Mathematica Stack Exchange is a question and answer site for users of Mathematica. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

In order to simplify my expression, I face a list manipulation problem.

For example, given the input list

list = {a[1] cof[1], a[2] cof[2], a[3] cof[3], a[4] cof[4], a[5] cof[5], a[6] cof[6]};

I can calculate the values of f[a[i]] and then combine the idential elements (f[] is just a function of the a[]). The condition of this transformation is

f[a[1]] == f[a[3]] == f[a[6]]; f[a[2]] == f[a[4]];

so that the output list becomes

newlist = {(cof[1] + cof[3] + cof[6]) a[1], (cof[2] + cof[4]) a[2], cof[5] a[5]};

How can I get this kind transformation to newlist for the general case?

share|improve this question
up vote 5 down vote accepted

Starting with your list and a dummy f that produces output with the equivalences you specified:

list = {a[1] cof[1], a[2] cof[2], a[3] cof[3], a[4] cof[4], a[5] cof[5], a[6] cof[6]};

f[a[1] | a[3] | a[6]] = "one";
f[a[2] | a[4]] = "two";

We might proceed:

GatherBy[list, f /@ Cases[#, _a] &]
{{a[1] cof[1], a[3] cof[3], a[6] cof[6]}, {a[2] cof[2], a[4] cof[4]}, {a[5] cof[5]}}
GatherBy[#, Head] & /@ List @@@ Join @@@ %
{{{a[1], a[3], a[6]}, {cof[1], cof[3], cof[6]}},
 {{a[2], a[4]}, {cof[2], cof[4]}}, {{a[5]}, {cof[5]}}}
First[#] Total[#2] & @@@ %
{a[1] (cof[1] + cof[3] + cof[6]), a[2] (cof[2] + cof[4]), a[5] cof[5]}
share|improve this answer
Thanks! This is very useful for me.In your second step, can you change it a little for a general case.Because cof[i_] is not a single Integer or Symbol, for example, cof[i_] := g[i] coff[i] const. – Orders Feb 24 '13 at 2:30
@Orders Does GatherBy[#, Head@# === a &] work for you? Also, be aware that GatherBy lists element groups by the order in which they first appear so if a[*] is not always first the third step will break; you may need to add a sort. If you include a more complex example in your question I'll try to address it. – Mr.Wizard Feb 24 '13 at 7:44
Sorry for the delay. Thanks for your help. You method is very general and it works for me now. Yes, I am dealing a complex expression, at least for me. I am facing a problem for simplification for large expression. If I cannot deal with it myself, I will post it again. – Orders Feb 26 '13 at 13:46
@Orders Okay, no problem. – Mr.Wizard Feb 26 '13 at 13:48

If you happen to dislike GatherBy as I do.

list = {a[1] cof[1], a[2] cof[2], a[3] cof[3], a[4] cof[4], a[5] cof[5], a[6] cof[6]};

Now similar to Mr. Wizard we define a function f which works as you suggest.

f[a[1] | a[3] | a[6]] = 1;
f[a[2] | a[4]] = 2;
f[_] := 0

And a second helper function g which does the actual work.

g[{0, expr_}] := expr
g[{i_, expr_}] := a[i]*Total[Cases[expr, _*r_cof :> r]]

Finally we put it all together...

With[{mask = f /@ Cases[list, a[r_]*_ :> a[r]]},
   g[{#, Pick[list, mask, #]}] & /@ DeleteDuplicates[mask]

(*{a[1] (cof[1] + cof[3] + cof[6]), a[2] (cof[2] + cof[4]), {a[5] cof[5]}} *)
share|improve this answer
Andy, +1 for a nice method, but why the dislike of GatherBy? Also, would a[r_]*__ (emphasis __) make this more general? – Mr.Wizard Feb 23 '13 at 4:37
@Mr.Wizard I suppose the __ would make it more general. I don't like GatherBy because it tends to be too slow for my taste. It may well have improved but my initial impression wasn't a good one. – Andy Ross Feb 23 '13 at 4:40
Strange; Tally and GatherBy have always seemed fast to me, compared to what I used before they were introduced. – Mr.Wizard Feb 23 '13 at 4:43
@Mr.Wizard Tally is incredibly fast, no complaints there (unless you want to transpose it). GatherBy I've never been happy with. – Andy Ross Feb 23 '13 at 4:47
Can you give me a simple example where GatherBy is slow and a faster alternative? I know one is if the compare function is much faster in vector mode; anything else? – Mr.Wizard Feb 23 '13 at 4:54

This is a pretty clumsy way but it works:

# /. a[_] -> First@Cases[#, a[_], 2, 1] & /@ Apply[Plus, 
       GatherBy[list, f@First@Cases[#, a[_], 1, 1] &],1]//Simplify

Step by step:

list = {a[1] cof[1], a[2] cof[2], a[3] cof[3], a[4] cof[4], a[5] cof[5], a[6] cof[6]};
f[x_a] := MemberQ[{1, 3, 6}, First@x]

(* For each element of the list it picks out the first (and only) a[i]
   and applies f to it. Then does what the function name suggests *)
gb=GatherBy[list, f@First@Cases[#, a[_], 1, 1] &]
(* {{a[1] cof[1], a[3] cof[3], a[6] cof[6]},
    {a[2] cof[2], a[4] cof[4], a[5] cof[5]}} *)

(* Add the elemens of each group *)
(* {a[1] cof[1] + a[3] cof[3] + a[6] cof[6], 
    a[2] cof[2] + a[4] cof[4] + a[5] cof[5]} *)

(* For each group replace all a[i] with just the first one *)
rep = # /. a[_] -> First@Cases[#, a[_], 2, 1] & /@ ap
(* {a[1] cof[1] + a[1] cof[3] + a[1] cof[6], a[2] cof[2] + a[2] cof[4] + a[2] cof[5]} *)

 (* {a[1] (cof[1] + cof[3] + cof[6]), a[2] (cof[2] + cof[4] + cof[5])} *)
share|improve this answer
 list = Array[a[#] cof[#] &, {6}];

 Simplify /@ Total /@ GatherBy[#, #[[1, 1]] &] &@
   MapAt[# /. Join[Thread[{1, 3, 6} -> 1], Thread[{2, 4} -> 2]] &, list, {All, 1}]


 Collect[#, a[_]] & /@ Total /@ GatherBy[#, #[[1, 1]] &] &[
   list /. {PatternSequence[a[1 | 3 | 6] x_] :> a[1] x, 
        PatternSequence[a[2 | 4] x_] :> a[2] x}]
 (* {a[1] (cof[1] + cof[3] + cof[6]), a[2] (cof[2] + cof[4]), a[5] cof[5]} *)
share|improve this answer

Trying to write a compact version. Your initial data:

l = {a[1] cof[1], a[2] cof[2], a[3] cof[3], a[4] cof[4], a[5] cof[5], a[6] cof[6]};
p = {f[a[1]] == f[a[3]] == f[a[6]], f[a[2]] == f[a[4]]};

And then just:

List @@ Simplify[ Total[l /. Flatten[Thread[# -> #[[1]]] & /@ (List @@@ p /. f@x_ -> x)]]]

{a[2] (cof[2] + cof[4]), a[5] cof[5], a[1] (cof[1] + cof[3] + cof[6])}
share|improve this answer

Your Answer


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.