2
$\begingroup$

I have a list

s:={1, 2, 4, 7, 8}

and I wish to replace every element in the list with Range[Max[s]].

I know that I could replace every element individually using ReplaceAll by doing

Flatten[s /. {1 -> Range[Max[s]], 2 -> Range[Max[s]], 
   4 -> Range[Max[s]], 7 -> Range[Max[s]], 8 -> Range[Max[s]]}]

However, s is arbitrary, and I will be using this for lists much longer than s. I would like to be able to perform this with one command, rather than replacing each element individually.

I have tried

s /. s -> Range[Max[s]]

But this just returns the range once. I haven't been able to find anything about replacing every element of a list.

$\endgroup$
4
$\begingroup$

Is this acceptable?

s={1, 2, 4, 7, 8};
Flatten[Map[Range[Max[s]]&,s]]

which is the same as

Flatten[Range[Max[s]]&/@s]

and both produce

{1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8}

which matches what your example code produces.

Map or /@ will replace each item in the list with the result of a function applied to the item in the list. In your case the function doesn't depend on any individual item, but on the Max of the whole list. And the & turns Range[Max[s]] into a function. And the final enclosing Flatten gets rid of the nesting of the individual lists.

Does that help explain the thinking?

$\endgroup$
1
  • $\begingroup$ Yes! Thank you! $\endgroup$ – user73377 Jun 17 '20 at 7:38
9
$\begingroup$

Using Replace(All) or Map is kinda slow for larger lists. Why not simply use:

ConstantArray[Range[Max[s]], Length[s]]
$\endgroup$
6
$\begingroup$

With ReplaceAll you need a pattern to restrict the replacement or it will match the entire list:

Flatten[s /. _Integer -> Range[Max@s]]
{1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, \
 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8}

Performance

SHuisman's answer reminded me of performance. If that is important consider PadRight:

PadRight[Range[1], #*Length[s], Range@#] &[Max@s]

Range[1] is used instead of {} as the seed for PadRight because it creates a packed array.

Benchmark:

Needs["GeneralUtilities`"]

fn1[s_List] := Flatten @ ConstantArray[Range[Max[s]], Length[s]]

fn2[s_List] := PadRight[Range[1], #*Length[s], Range@#] &[Max@s]

BenchmarkPlot[{fn1, fn2}, RandomInteger[9, #] &]
BenchmarkPlot[{fn1, fn2}, RandomInteger[#, 1*^5] &]

enter image description here enter image description here

$\endgroup$
1
  • $\begingroup$ PadRight is surprisingly fast! Wow! Flatten@, Join@@ or Catenate@ btw have quite different timings. but your PadRight stays faster… $\endgroup$ – SHuisman Jun 17 '20 at 12:33
3
$\begingroup$
Mod[Range[Length[s] #], #, 1] & @ Max[s]
{1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 
 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8}
First @ ArrayReshape[Range @ #, {1, Length[s] #}, "Periodic"] & @ Max[s]
{1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 
 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8}
$\endgroup$
2
$\begingroup$
(* Not a one-liner, but easy enough to do so *)
s = {1, 2, 4, 7, 8};

l = Length[s];

item = Range[Max[s]]

Replace[s, {_ -> item}, {1}]  (* use level spec *)

(* Flatten if desired *)
$\endgroup$
1
  • 1
    $\begingroup$ l = Length[s]; does not appear to be used? Also consider Cases[s, _ -> item] as Cases defaults to a levelspec of {1}. $\endgroup$ – Mr.Wizard Jun 20 '20 at 13:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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