7
$\begingroup$

I have a matrix made mostly from "x" with a few 0's thrown in:

list = {{0, "x", "x", 0}, {0, "x", "x", "x"}, {0, "x", "x", 
   "x"}, {"x", "x", "x", "x"}, {"x", "x", "x", "x"}, {"x", "x", "x", 
   "x"}, {"x", "x", "x", "x"}, {"x", "x", "x", "x"}, {0, "x", "x", 
   "x"}}

I know that there are 31 "x"'s in the matrix, and I want to replace each of them with a numbered variable: x1, x2, and so on til x31 (without quotations). Here's the expected result:

(* {{0, x1, x2, 0}, {0, x3, x4, x5}, {0, x6, x7, x8}, {x9, x10, x11, 
  x12}, {x13, x14, x15, x16}, {x17, x18, x19, x20}, {x21, x22, x23, 
  x24}, {x25, x26, x27, x28}, {0, x29, x30, x31}} *)

Below is the list of the numbered variables that you may use:

numberedx = Table["x" <> ToString@i // Symbol, {i, Range@Count[list, "x", {2}]}]
(* {x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, 
x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, 
x21, x22, x23, x24, x25, x26, x27, x28, x29, x30, x31} *)

I can't for the life of me figure out how to do this. My gut tells me that some procedural method is in order since each "x" is replaced by its numbered form, then the next "x" down the line is replaced by the next member in the list of numbered variables. Unfortunately I'm very uncomfortable with procedural programming. I also tried MapIndexed but the 0's are mapped as well. I'd really appreciate any help or insight.

$\endgroup$

5 Answers 5

7
$\begingroup$

ReplaceAll solution:

i = 1;
list /. "x" :> Symbol@StringJoin["x", ToString[i++]]

The same thing can be achieved with Map:

i = 1;
Map[If[# == "x", "x" <> ToString[i++] // Symbol, #] &, list, {2}]

They both give:

{{0, x1, x2, 0}, {0, x3, x4, x5}, {0, x6, x7, x8}, {x9, x10, x11, 
      x12}, {x13, x14, x15, x16}, {x17, x18, x19, x20}, {x21, x22, x23, 
      x24}, {x25, x26, x27, x28}, {0, x29, x30, x31}}
$\endgroup$
0
6
$\begingroup$

Pickett's answer should get the job done but I encourage you to use indexed objects:

Module[{i = 0},
  list /. s_String :> x[++i]
]
$\endgroup$
4
  • $\begingroup$ I'm not sure why the replacement would not stop after the first "x" is replaced by x1. Does the ++ have anything to do with it? $\endgroup$ Commented Aug 8, 2014 at 21:04
  • $\begingroup$ @seismatica I don't know why you think only the first "x" would be replaced. /. is ReplaceAll. ++i is used to increment a counter with each replacement that is made. (Or each replaced expression that evaluates, to be more correct.) I am sorry if that is not helpful, but I don't understand your confusion so I find it hard to address. $\endgroup$
    – Mr.Wizard
    Commented Aug 8, 2014 at 21:08
  • $\begingroup$ For some reason I thought I have to use //. because sometimes /. might only replace the first instance. I guess that's the problem only for significant replacements that alters large interconnected parts of the expression, so it wouldn't be the problem here as only a single x is replaced. $\endgroup$ Commented Aug 8, 2014 at 21:20
  • $\begingroup$ @seismatica It is not really about "large interconnected parts" but: (1) simple precedence of rules and (2) direction of traversal. By the first I mean something like this: {1, 1, 1} /. {_Integer -> 1, 1 -> 2} where 2 will never appear in the output because the first rule will match anything that the second will and it is applied first. //. will not change that. By the second I mean e.g. {1, 2, 3} //. {x_Integer :> x + 1, x_List :> Mod[x, 2]}. Here the entire expression is tested and found to match the second rule; the first rule is not also applied to the replaced expression. $\endgroup$
    – Mr.Wizard
    Commented Aug 9, 2014 at 2:40
4
$\begingroup$

Another way:

Module[{i = 0, l = #, f}, 
   f[x_String] := Symbol[x <> ToString[++i]];
   f[x_] := x;
   Map[f, l, {2}]] &@list
{{0, x1, x2, 0}, {0, x3, x4, x5}, {0, x6, x7, x8}, {x9, x10, x11, x12},
 {x13, x14, x15, x16}, {x17, x18, x19, x20}, {x21, x22, x23, x24},
 {x25, x26, x27, x28}, {0, x29, x30, x31}}
$\endgroup$
3
$\begingroup$

Another way using ReplacePart:

pos = Position[list, "x"];

idx = ToString /@ Range[Length@pos];

numbdx = Symbol@*StringJoin @@@ Thread[{Array["x" &, Length@#], #}] &@idx;

ReplacePart[list, Thread[pos -> numbdx]]

(*{{0, x1, x2, 0}, {0, x3, x4, x5}, {0, x6, x7, x8}, {x9, x10, x11, x12}, 
   {x13, x14, x15, x16}, {x17, x18, x19, x20}, {x21, x22, x23, x24}, 
   {x25, x26, x27, x28}, {0, x29, x30, x31}}*)
$\endgroup$
1
$\begingroup$

I worked out a solution using Position and a Table of Set's i.e. "set x at this position to the numbered variable with the same index i". I really appreciate all of your answers, and hopefully I will get used to using ++i in the future.

Clear[replacex]
replacex[l_List] := Module[{lCopy = l, positionx, numberedx},
  positionx = Position[l, "x"]; 
  numberedx = Table[Symbol["x" <> ToString[i]], {i, Range[Count[l, "x", {2}]]}]; 
  Table[lCopy[[Sequence @@ positionx[[i]]]] = numberedx[[i]], {i, Length[numberedx]}];
  lCopy]

replacex[list]
(* {{0, x1, x2, 0}, {0, x3, x4, x5}, {0, x6, x7, x8}, {x9, x10, x11, 
  x12}, {x13, x14, x15, x16}, {x17, x18, x19, x20}, {x21, x22, x23, 
  x24}, {x25, x26, x27, x28}, {0, x29, x30, x31}} *)

I also found that these work to replace the second to last line in my module:

Function[i, lCopy[[Sequence @@ positionx[[i]]]] = numberedx[[i]]] /@ Range@Length[numberedx];
Set[lCopy[[Sequence @@ positionx[[#]]]], numberedx[[#]]] & /@ Range@Length[numberedx];

but not this

(lCopy[[Sequence @@ positionx[[#]]]] = numberedx[[#]] &) /@ Range@Length[numberedx];

Mathematica graphics

$\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.