[SCSS|Sass] 文字列型の数字をちゃんと数値型にしたい(2) 複数桁に対応
前回の続き。一桁の値しか変換できなかったので複数桁の変換に対応させる。
引数で受け取った文字数の分、繰り返し変換する機能を実装する。
話が長くなるので、とりあえず先に完成品を掲載しておく。
自作関数 今回の目標の完成品
@function str2num($val) {
@if number == type-of($val) {
@return $val;
} @else if string != type-of($val) {
@warn "#{$val} is not string.";
@return $val;
}
$num-map: (
'0' : 0,
'1' : 1,
'2' : 2,
'3' : 3,
'4' : 4,
'5' : 5,
'6' : 6,
'7' : 7,
'8' : 8,
'9' : 9,
);
$num: 0;
$tmp: 0;
$length: str-length($val);
@for $i from 0 to $length {
$pos: $i;
$tmp: str-slice($val, $length - $i, $length - $i);
$tmp: map-get($num-map, $tmp);
@if not $tmp {
@warn "'#{$val}' is not string type numbers.";
$tmp: $val;
}
$digit: 1;
@if 0 < $pos {
@for $k from 1 through $pos {
$digit: $digit * 10;
}
$tmp: $tmp * $digit;
}
$num: $num + $tmp;
}
@return $num;
}
解説
変換中に使用する変数を初期化
$num: 0;
$tmp: 0;
$num: 0;
数値型に変換した結果を保存する変数。ゼロで初期化。
最終的にこれをリターンする。
$tmp: 0;
引数で受け取った数字を1文字ずつ取り出して変換するので、これから処理する文字を保存する一時保存用の変数。
受け取った文字数を取得
$length: str-length($val);
1文字ずつ変換の作業を繰り返すため、何文字あるのか調べる。
文字数分繰り返すループ処理
@for $i from 0 to $length {}
for ループはカウントを取りながら {} の中の処理を繰り返す。
ここでは変数 $i でカウントする。
0 から開始して $length(文字数が代入されている)まで。
3文字あれば、 0 から 3 まで 4回繰り返す。3文字なのに4回なの? という疑問は以下のループ内の処理の説明にて。
★ループ内の処理
何桁目か記録する
$pos: $i;
複数桁に対応するため、何桁目を処理してるのか記録しておく。
処理する一文字を取得
$tmp: str-slice($val, $length - $i, $length - $i);
str-slice() は 文字列から指定された区間の文字を取り出す。
@see https://sass-lang.com/documentation/modules/string/#slice
ここでは文字列の末尾から処理するため $length - $i の計算により取得位置を逆順にしている。
3文字を変換する場合
$length - $i :
3 - 0 = 3(3文字目)
3 - 1 = 2(2文字目)
3 - 2 = 1(1文字目)
3 - 3 = 0(0文字目)
ゼロ文字目は存在しないので実質処理されない。
マップから $tmp を検索
// 変更前: $tmp: map-get($num-map, $val);
$tmp: map-get($num-map, $tmp);
前回は引数の $val を直接検索していたが、取り出した1文字を対象に検索するようコードを変更。
桁処理用の変数を初期化
$digit: 1;
桁処理の計算に使う。初期値は 1 で、ループ毎に初期化される。
十の位からの分岐処理
@if 0 < $pos {}
一の位以上は桁処理が必要になる。
$pos が 0 のときは一の位、 1 のときは十の位を処理しているので、0 より大きいときに実行する分岐を追加。
桁処理
@for $k from 1 through $pos {
$digit: $digit * 10;
}
ループ内に2個目のループ。
$digit * 10 で桁を上げる計算。十の位なら1回、百の位なら2回繰り返す。
1個目のループと同じ @for だが「$k from 1 through $pos 」になっている。
"through" と "to" はカウントの最後にループ処理をするかどうかが違う。
$tmp: $tmp * $digit;
10 や 100 といった桁が求められたら、取り出していた1桁と掛ける。
十の位の値は 10~90 または 0 になる。
$num: $num + $tmp;
$num を合計として、各桁で求めた値を加算していく。
$num をリターン
// 変更前: @return $tmp;
@return $num;
前回は $tmp をマップの検索結果にしていたが、今回は $num に変換結果が保存されている。
これで以下のような結果が得られる。
$val: "123450";
$val:str2num($val);
@debug "#{$val} #{type-of($val)}";
> Debug: 123450 number