# How to combine some elements of a list?

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

For example, given the input list

list = {a cof, a cof, a cof, a cof, a cof, a cof};


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] == f[a] == f[a]; f[a] == f[a];


so that the output list becomes

newlist = {(cof + cof + cof) a, (cof + cof) a, cof a};


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

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

list = {a cof, a cof, a cof, a cof, a cof, a cof};

f[a | a | a] = "one";
f[a | a] = "two";


We might proceed:

GatherBy[list, f /@ Cases[#, _a] &]

{{a cof, a cof, a cof}, {a cof, a cof}, {a cof}}

GatherBy[#, Head] & /@ List @@@ Join @@@ %

{{{a, a, a}, {cof, cof, cof}},
{{a, a}, {cof, cof}}, {{a}, {cof}}}

First[#] Total[#2] & @@@ %

{a (cof + cof + cof), a (cof + cof), a cof}

• 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 cof, a cof, a cof, a cof, a cof, a cof};


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

f[a | a | a] = 1;
f[a | a] = 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]]},
]

(*{a (cof + cof + cof), a (cof + cof), {a cof}} *)

• 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 cof, a cof, a cof, a cof, a cof, a cof};
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 cof, a cof, a cof},
{a cof, a cof, a cof}} *)

(* Add the elemens of each group *)
ap=Apply[Plus,gb,1]
(* {a cof + a cof + a cof,
a cof + a cof + a cof} *)

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

Simplify@rep
(* {a (cof + cof + cof), a (cof + cof + cof)} *)

 list = Array[a[#] cof[#] &, {6}];

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


or

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


Trying to write a compact version. Your initial data:

l = {a cof, a cof, a cof, a cof, a cof, a cof};
p = {f[a] == f[a] == f[a], f[a] == f[a]};


And then just:

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

(*
{a (cof + cof), a cof, a (cof + cof + cof)}
*)