3

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@
EminemIsLife
  • 167
  • 1
  • 4

6 Answers6

9
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}
]
Kuba
  • 136,707
  • 13
  • 279
  • 740
4

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}

nben
  • 2,148
  • 9
  • 20
1

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
@|<>|#|&

e.doroskevic
  • 5,959
  • 1
  • 13
  • 32
1
Rest@Table[If[Mod[i - 1, 3] == 0, "string" <> ToString@i, i], {i, 0, 9}]

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

eldo
  • 67,911
  • 5
  • 60
  • 168
1

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}
J. M.'s missing motivation
  • 124,525
  • 11
  • 401
  • 574
1

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.

Pankaj Sejwal
  • 2,063
  • 14
  • 23