# Long list of similar rules

Given the following:

Clear[a, b, c, d, e, f, g, h];

{a, b, c, d, e, f, g, h} /. {a -> 12, c -> 12, e -> 12, f -> 12}

(* {12, b, 12, d, 12, 12, g, h} *)


Is there a more elegant or shorter notation to replace multiple symbols with a similar value?

• These kind of problems are best solved using the Map function. Jan 26, 2015 at 9:37

You can have a single rule using Alternatives (|)...

{a, b, c, d, e, f, g, h} /. x : (a | c | e | f) -> 12


Furthermore you can construct the rule on the fly...

{a, b, c, d, e, f, g, h} /. x : Alternatives@@{a, c, e, f} -> 12


As noted by @Kuba below there is no requirement for the pattern to have a name (x) so...

{a, b, c, d, e, f, g, h} /. (a | c | e | f) -> 12
{a, b, c, d, e, f, g, h} /. Alternatives @@ {a, c, e, f} -> 12


also work.

• Now that was kind of alternative (no pun intended) I was hoping for. Thanks Ymareth. Jan 26, 2015 at 9:44
• There is no need to name those patterns with x:.
– Kuba
Jan 26, 2015 at 9:47
• The second alternative maybe even more useful since you can use a part of the original list. Jan 26, 2015 at 9:47
• @MathLind But keep the pattern name if you want to use the symbols as arguments, ie. x : (a | c | e | f) :> 12 x.
– BoLe
Jan 26, 2015 at 20:11

I like using Thread for this kind of rules

{a, b, c, d, e, f, g, h} /. Thread[{a, c, e, f} -> Table[12, {4}]]

As @kguler suggests there is no need having a Table and keeping track of the length of the list also for a single value.

So the following is better!

{a, b, c, d, e, f, g, h} /. Thread[{a, c, e, f} -> 12]

• Simpler Thread[{a, c, e, f} -> 12] also works.
– kglr
Jan 26, 2015 at 11:46
• You are right! I see it's because of head h being a List by default on Thread[{a,c,e,f}->11,h] Jan 26, 2015 at 12:10
• @MathLind, i think it might be better if tchronis could update -- if he agrees to - his answer adding the alternative syntax for Thread.
– kglr
Jan 26, 2015 at 16:32
• Added @kugler 's syntax. Jan 26, 2015 at 16:37

You asked for "more elegant or shorter" and Ymareth gave you exactly that. However it is worth noting that with long Alternatives this comes at a performance cost. Please observe:

Needs["GeneralUtilities"]

set = Range[20000];

fn1[x_] := set /. (Alternatives @@ x -> -1)
fn2[x_] := set /. (Dispatch @ Thread[x -> -1])

BenchmarkPlot[{fn1, fn2}, RandomChoice[set, #] &, 2^Range[14]]


If you do not include the time taken by Dispatch to optimize the table the second method has an even larger advantage, which is applicable if the rules will be used more than once.

As a guideline if it is short enough to type using a | b | c | . . . you're fine, but if using Alternatives @@ at least consider Dispatch.

Ymareth wrote:

It would be interesting to know, if at extreme values of n, the gradients are the same; dispatch just has a much lower constant multiplier or dispatch's cost is growing slower regardless of n.

The complexity of Thread and Dispatch seems pretty consistent:

set = List @@ Range[5*^6]; (* unpacked *)

BenchmarkPlot[{Dispatch@Thread[# -> -1] &}, RandomChoice[set, #] &, 2^Range[22],
"IncludeFits" -> True, TimeConstraint -> 60]


The complexity of replacement itself however seems to increase with the number of rules:

set = List @@ Range[5*^6];  (* unpacked *)

BenchmarkPlot[{set /. # &}, Dispatch@Thread[# -> -1] &@RandomChoice[set, #] &,
2^Range[22], TimeConstraint -> 60]


Perhaps the constant-time floor is the cost to apply a single rule to the five million element list and it takes ~= 10^5 rules before the cost of checking that many rules rises above the floor. I'll try to think of other ways to probe this behavior.

• +1 for using Dispatch and the BenchmarkPlot tool. It seems that I have to quit the kernel and re-run when removing Dispatch@ from fn2 (Even when I use ClearAll[fn2] before that. I suppose it has something to do with the internals of BenchmarkPlot ? Jan 27, 2015 at 6:24
• @tchronis Thanks for the vote. BenchmarkPlot caches its results. I believe using slightly different input values will force a fresh run; for example you could use 2^Range[14] - 1. Jan 27, 2015 at 6:56
• Thanks. Changing the name of one function also does the trick. Interesting! But is there a way to clear the cache? Anyway probably there are a lot of questions to be asked in only a comment. Jan 27, 2015 at 7:00
• @tchronis I like questions or I wouldn't be on this site. ;-) A bit of spelunking suggests that this should do it: Clear[GeneralUtilitiesBenchmarkingPackagePrivate\$TimingCaches] Jan 27, 2015 at 7:04
• It would be interesting to know, if at extreme values of n, the gradients are the same; dispatch just has a much lower constant multiplier or dispatch's cost is growing slower regardless of n. Jan 27, 2015 at 8:51

MapAt on alternatives pattern:

With[{
list = {a, b, c, d, e, f, g, h},
sub = {a, c, e, f}},
MapAt[f, list,
Position[list, Alternatives @@ sub]]]


Replace f with 12& for a constant-12 function.