11
$\begingroup$

I have a list of numbers like

{-6, 1, 3, 23}

and want to get a list of strings, all of the same length, with zeros padding on the left as needed, like

{"-6", "01", "03", "23"}

The closest I can get following the documentation is with something like

NumberForm[{-6, 1, 3, 23}, 1, NumberPadding -> {"0", ""}]

which (astonishingly) produces

{"-6", "01", "03", "023"}

How I can simply get my strings to all be the same length, padded as needed with zeros?

$\endgroup$
8
  • 1
    $\begingroup$ What do you desire for the first entry: "-06" or "0-6" or "-006"? $\endgroup$ Commented Mar 3, 2015 at 18:24
  • 1
    $\begingroup$ IntegerString may come in handy. $\endgroup$
    – Yves Klett
    Commented Mar 3, 2015 at 18:41
  • 1
    $\begingroup$ What in case of: {-6, 123}? $\endgroup$
    – Kuba
    Commented Mar 3, 2015 at 18:46
  • 1
    $\begingroup$ @raxacoricofallapatorius Do you want every and all strings to be of length 2 in every case (not just in your example)? You never want to apply the transformation to numbers such as 123 or -11? $\endgroup$ Commented Mar 3, 2015 at 18:46
  • 1
    $\begingroup$ @DavidG.Stork: Just like the example. Never more than one digit negative numbers and never more than two digit positives. $\endgroup$
    – orome
    Commented Mar 3, 2015 at 18:51

7 Answers 7

13
$\begingroup$

As of 10.1 this is built in to Mathematica with StringPadLeft:

StringPadLeft[#,2,"0"]&@*ToString/@{-6,1,3,23}

{"-6", "01", "03", "23"}

$\endgroup$
2
  • $\begingroup$ Could you please elucidate on the meaning of &@*? $\endgroup$
    – Oscillon
    Commented Sep 15, 2019 at 1:41
  • 2
    $\begingroup$ @Oscillon Composition $\endgroup$
    – orome
    Commented Sep 15, 2019 at 1:46
6
$\begingroup$
ClearAll[nF]
nF = With[{l = #, p = #2}, NumberForm[l, p, SignPadding -> True, NumberPadding -> {"0", ""}, 
         NumberFormat -> (StringTake[#1, -(p + 1)] &)]] &;

nF[{-6, 1, 3, 23}, 1]
(* {-6, 01, 03, 23} *)

nF[{-6, 1, 3, 23, 123}, 2]
(* {-06, 001, 003, 023, 123} *)

Note: You can also use PaddedForm instead of NumberForm.

$\endgroup$
4
$\begingroup$

Taking under consideration your assumptions:

StringTake["0" <> ToString[#], -2] & /@ {-6, 1, 3, 23}
$\endgroup$
2
$\begingroup$

One way:

If[StringLength@# == 1, "0" <> #, #] &@*ToString /@ {-6, 1, 3, 23}

Another way:

StringJoin@*(ToString /@ PadLeft[#, 2] &)@*Characters@*ToString /@ {-6, 1, 3, 23}
$\endgroup$
9
  • $\begingroup$ So there's no built-in way to do this? Any idea what the reasoning behind the way NumberForm works could possibly be? I really don't understand the philosophy behind the Mathematica language design (c.f.: [str(n).zfill(2) for n in numbers]). $\endgroup$
    – orome
    Commented Mar 3, 2015 at 18:42
  • $\begingroup$ @raxacoricofallapatorius Take a look at the newest solution. There is a built in function, but in order to use it we must first convert our string into a list. $\endgroup$
    – C. E.
    Commented Mar 3, 2015 at 18:52
  • $\begingroup$ So the second solution is incomplete. $\endgroup$
    – orome
    Commented Mar 3, 2015 at 18:58
  • $\begingroup$ @raxacoricofallapatorius Argh, I forgot to convert it back into a string. Now it's less elegant; I'd go with the first option. I'd go with it anyway because it's easy to understand; if you look at it in 6 months you will still know what it does. $\endgroup$
    – C. E.
    Commented Mar 3, 2015 at 19:00
  • $\begingroup$ Thanks. That works. I'm still puzzled what (if any) design philosophy could have lead to this sort of thing. [str(n).zfill(2) for n in numbers] is not only something I'll understand in 6 months, but something that I can remember to recreate in 6 months (or years). $\endgroup$
    – orome
    Commented Mar 3, 2015 at 19:05
2
$\begingroup$

just for fun..we can define a zfill function

 zfill[n_, f_String: "0"] := 
      Function[{s}, 
        StringJoin[ConstantArray[f, Max[0, n - StringLength[s]]], s]];

then the operation is quite similar to your python expression:

 zfill[2] /@ ToString /@ {-6, 1, 3, 23}

{"-6", "01", "03", "23"}

$\endgroup$
1
  • $\begingroup$ I find this a good one! $\endgroup$
    – Santiago
    Commented Mar 19, 2015 at 10:32
2
$\begingroup$

Here is an answer that is loosely based upon kglr's previous answer. The below solution has been generalized to also handle decimal numbers and scientific form. Your example number list only contained small integers, but others who find this post may find the generalization useful.

The code:

AltNumberFormat[n_, f___, 
  opts : OptionsPattern[{NumberForm, "AltPadding" -> ""}]] := With[{
   IsExtraPad = 
    MatchQ@First[OptionValue@NumberPadding /. Automatic -> {""}],
   altPad = OptionValue@"AltPadding"
   },
  NumberForm[
   N@n, f, FilterRules[{opts}, Options@NumberForm],
   NumberFormat -> Function[{m, b, e}, With[
      {s = 
        StringReplace[m, StartOfString ~~ _?IsExtraPad -> altPad]},
      If[e == "", s, Row[{s, b^e}, "\[Times]"]]
      ]]]]

Usage is exactly the same as regular NumberForm, with the addition of an extra AltPadding option. This would usually be used if you wanted to pad with leading zeros (without the extra zeros!) but still get your numbers to left-align, by padding positive numbers with leading spaces.

Test cases:

hdr = Prepend[
   Item[#, BaseStyle -> Bold, Background -> LightBlue] & /@ {"Input", 
     "Format", "Padding", "Output"}];

Row@Map[Grid[hdr@#, Frame -> All, Alignment -> Left, 
       Background -> {{4 -> LightYellow}}] &]@
    Transpose@Flatten[#, 2] &@
 Table[
  {m*n, f, Style[p, ShowStringCharacters -> True], 
   AltNumberFormat[m*n, f, NumberPadding -> p, SignPadding -> True, 
    AltPadding -> " "]},
  {f, {Unevaluated@Sequence[], 4, {6, 3}}},
  {n, {0.1, 1.2, 1.23456, 123456, 1.2*10^15, 1.23456*10^15}},
  {m, {-1, 1}},
  {p, {Automatic, {"", "0"}, {"0", "0"}}}
  ]

Output:

enter image description here

$\endgroup$
0
$\begingroup$

Using sprintf that I describe in this answer, you can do Map[sprintf["%02i", #] &, {-6, 1, 3, 23}] to get {"-6", "01", "03", "23"}.

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