3
$\begingroup$

How do I join a string to the first and every third element in the list?

Example:

list={1,2,3,4,5,6,7,8,9..}
={"string1",2,3,"string4",5,6,"string7",8,9..}

I have come to the conclusion that this would be some of the solution, but I don't know how to do it every third element.

"string"<>ToString@
$\endgroup$
1

6 Answers 6

4
$\begingroup$

There are a lot of ways to do this, but here is one:

Method1[list_] := Fold[
  Function@ReplacePart[#1, #2 -> ("string" <> ToString[#1[[#2]]])],
  list,
  Range[1, Length[list], 3]];
Method1 @ Range[9]

{"string1", 2, 3, "string4", 5, 6, "string7", 8, 9}

For help understanding this, see the Fold function, and the ReplacePart function. This is a rather functional way of solving the problem; a more procedural method might look like this:

Method2[listArg_] := Module[
 {list = listArg},
 Do[
  list[[i]] = "string" <> ToString[list[[i]]],
  {i, 1, Length[list], 3}];
 list];
Method2 @ Range[9]

{"string1", 2, 3, "string4", 5, 6, "string7", 8, 9}

You can also set the list directly, if list is not a function argument or a constant:

Method3[listArg_] := Module[
 {list = listArg},
 list[[1 ;; All ;; 3]] = ("string" <> ToString[#]) & /@ list[[1 ;; All ;; 3]];
 list];
Method3 @ Range[9]

{"string1", 2, 3, "string4", 5, 6, "string7", 8, 9}

And, one final way:

Method4[list_] := MapAt[
   Function["string" <> ToString[#]],
   list, 
   List /@ Range[1, Length[list], 3]];
Method4 @ Range[9]

{"string1", 2, 3, "string4", 5, 6, "string7", 8, 9}

Edit:

As pointed out by Rorschach, some of these methods are slow for large lists (specifically, methods 1, 2, and 4 are fairly slow). For completeness, I've placed Rorschach's method just below. The Method3 above runs faster in my own tests than Rorschach's proposed method (due, probably, to the partitioning in Rorschach's method), while methods 1 and 4 can be rearranged slightly to run in comparable time; a new method 1 is below, and method 4 can be adapted to look like the solution given by Kuba.

RorschachsMethod[list_] := Flatten@Map[
  {"string" <> ToString[#[[1]]], #[[2]], #[[3]]} &,
  Partition[r, 3]];

KubaMethod[list_] := MapAt[
  Function["string" <> ToString[#]],
  list,
  {1 ;; All ;; 3}];

NewMethod1[list_] := With[
  {range = Range[1, Length[list], 3]},
  ReplacePart[
    list,
    Thread[range -> (("string" <> ToString[#]) & /@ list[[range]])]]];

In case speed is an issue for anyone, and for the sake of completeness, the fastest version of this function I was able to come up with (without employing any parallel or compiling tools) is below:

FastMethod[listArg_] := Module[
  {list = listArg},
  list[[1 ;; All ;; 3]] = MapThread[
    StringJoin,
    {ConstantArray["string", Floor[(Length[list] + 2)/3]],
     ToString /@ list[[1 ;; All ;; 3]]}];
  list];

This last method runs slightly faster than the original Method3 and in about half the time (for argument Range[10^6]) of any other method I've seen:

With[
 {r = Range[10^6]},
 Table[
  fn -> First@AbsoluteTiming[fn[r]],
  {fn, {Method3, NewMethod1, RorschachMethod, KubaMethod, FastMethod}}]]

{Method3 -> 1.42383, NewMethod1 -> 2.59458, RorschachMethod -> 2.90768, KubaMethod -> 1.42317, FastMethod -> 1.26555}

$\endgroup$
2
  • $\begingroup$ Very slow for large lists. $\endgroup$ Commented Dec 9, 2015 at 8:33
  • 1
    $\begingroup$ @Rorschach: The third method is not slow; the others are slower, but this is a beginner question, so they are intended to show the many tools for this kind of operation. $\endgroup$
    – nben
    Commented Dec 9, 2015 at 16:36
9
$\begingroup$
f = "string" <> ToString[#] &

or, if you have 10+:

f = StringTemplate["string``"]

Can't find a duplicate, so:

list = Range@10;
list[[;; ;; 3]] = f /@ list[[;; ;; 3]];

list
{"string1", 2, 3, "string4", 5, 6, "string7", 8, 9, "string10"}

Or:

list = Range@10;

MapAt[
 f,
 list,
 {;; ;; 3}
]
$\endgroup$
2
  • 1
    $\begingroup$ Span + Part functionality is the best thing in MMA ;) $\endgroup$
    – Kuba
    Commented Dec 8, 2015 at 21:14
  • $\begingroup$ I really should start thinking along these lines more! $\endgroup$
    – sebhofer
    Commented Dec 8, 2015 at 22:49
1
$\begingroup$

Code:

data = Range @ 12;
Table[{i}, {i, 1, Length @ data, 3}];
MapAt["string" <> ToString @ # &, data, %]

Output:

{"string1", 2, 3, "string4", 5, 6, "string7", 8, 9, "string10", 11, 12}

Alternative (as one line):

MapAt["string" <> ToString @ # &, #, Table[{i}, {i, 1, Length @ #, 3}]] &[Range @ 12]

Output:

{"string1", 2, 3, "string4", 5, 6, "string7", 8, 9, "string10", 11, 12}

Reference:
MapAt
ToString
Table
Length
Range
@|<>|#|&

$\endgroup$
3
  • 1
    $\begingroup$ This is essentially the same as user21382's last solution. $\endgroup$
    – march
    Commented Dec 8, 2015 at 21:12
  • 2
    $\begingroup$ @march I agree, all of the solutions are essentially the same since they achieve same objective. The above has been produced only out of curiousity since I am still learning MMA. :) $\endgroup$ Commented Dec 8, 2015 at 21:19
  • $\begingroup$ Very very slow for large lists. $\endgroup$ Commented Dec 9, 2015 at 8:30
1
$\begingroup$
Rest@Table[If[Mod[i - 1, 3] == 0, "string" <> ToString@i, i], {i, 0, 9}]

{"string1", 2, 3, "string4", 5, 6, "string7", 8, 9}

$\endgroup$
1
$\begingroup$

I'm a bit sad nobody thought to use MapIndexed[]:

MapIndexed[With[{k = #2[[1]]}, If[Mod[k, 3] == 1, "string" <> ToString[k], #]] &,
           Range[9]]
   {"string1", 2, 3, "string4", 5, 6, "string7", 8, 9}
$\endgroup$
1
$\begingroup$

Partition into groups of three and just apply your operation on first of every sub list and flatten. Say for range of 10 elements, use patterns,

    ReplaceRepeated[Partition[Range[10],3], 
    {s___, {a_, b_, d_}, k___} :> {s, "string" <> ToString[a], b, d, k}, 
    MaxIterations -> Length[Ceiling[Range[10]/3]]]

=> {"string1", 2, 3, "string4", 5, 6, "string7", 8, 9}

But since we are using patterns, it slows down mostly with large data sets, Table version gives the fastest of all the answers.

AbsoluteTiming[
 Flatten[Map[{"string" <> ToString[#[[1]]], #[[2]], #[[3]]} &, 
   Partition[Range[1000000], 3]]]] 

giving 1.2649 seconds. I don't have V10+ so I couldn't check @Kuba solution.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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