10
$\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$ Mar 3 '15 at 18:24
  • $\begingroup$ @DavidG.Stork: "-6", as indicated: all the same length. $\endgroup$
    – orome
    Mar 3 '15 at 18:26
  • $\begingroup$ Oh... it was unclear whether you wanted all strings to have the length 3 (in this case)... as in "023". Let me work on this.... $\endgroup$ Mar 3 '15 at 18:29
  • 1
    $\begingroup$ IntegerString may come in handy. $\endgroup$
    – Yves Klett
    Mar 3 '15 at 18:41
  • 1
    $\begingroup$ @DavidG.Stork: Just like the example. Never more than one digit negative numbers and never more than two digit positives. $\endgroup$
    – orome
    Mar 3 '15 at 18:51
12
$\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
    Sep 15 '19 at 1:41
  • 2
    $\begingroup$ @Oscillon Composition $\endgroup$
    – orome
    Sep 15 '19 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
    Mar 3 '15 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.
    Mar 3 '15 at 18:52
  • $\begingroup$ So the second solution is incomplete. $\endgroup$
    – orome
    Mar 3 '15 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.
    Mar 3 '15 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
    Mar 3 '15 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
    Mar 19 '15 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, privacy policy and cookie policy

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