【JavaScript】改行と半角スペースが混ざった整数入力を受け取る方法
paizaの「整数を半角 + 改行区切りで入力 2」という問題に取り組みました。
今回のポイントは、入力がすべて同じ形式ではなく、改行区切りと半角スペース区切りが混ざっていることです。
問題
整数 L、u、a、b、v が、次の形式で入力されます。
L
u a b
v
それぞれの整数に 1 を足し、改行区切りで出力します。
入力例
7
8 0 2
1
出力例
8
9
1
3
2
書いたコード
process.stdin.resume();
process.stdin.setEncoding("utf8");
var lines = [];
var reader = require("readline").createInterface({
input: process.stdin,
output: process.stdout,
});
reader.on("line", (line) => {
lines.push(line);
});
reader.on("close", () => {
const input1 = lines[0];
const input2 = lines[1].split(" ");
const input3 = lines[2];
const L = Number(input1);
const u = Number(input2[0]);
const a = Number(input2[1]);
const b = Number(input2[2]);
const v = Number(input3);
console.log(L + 1);
console.log(u + 1);
console.log(a + 1);
console.log(b + 1);
console.log(v + 1);
});
このコードで正しく出力できます。
入力の中身を確認する
入力が次の場合を考えます。
7
8 0 2
1
linesの中には、1行ずつ文字列として保存されます。
lines[0]; // "7"
lines[1]; // "8 0 2"
lines[2]; // "1"
lines[0]とlines[2]には、それぞれ整数が1つだけ入っています。
一方で、lines[1]には3つの整数が半角スペース区切りで入っています。
8 0 2
そのため、2行目だけsplit(" ")を使って分割します。
const input2 = lines[1].split(" ");
分割後は、次の配列になります。
["8", "0", "2"]
それぞれ、添字を使って取り出せます。
input2[0]; // "8"
input2[1]; // "0"
input2[2]; // "2"
Number()を使う理由
標準入力から受け取った値は、数値ではなく文字列です。
たとえば、次の値も文字列として受け取ります。
lines[0]; // "7"
そのため、Number()を使って数値に変換します。
const L = Number(input1);
もし文字列のまま1を足すと、足し算ではなく文字列の結合になります。
"7" + 1;
結果は次のようになります。
71
数値に変換してから計算すると、正しく8になります。
Number("7") + 1;
8
処理の流れ
今回のコードでは、まず3行をそれぞれ取り出しています。
const input1 = lines[0];
const input2 = lines[1].split(" ");
const input3 = lines[2];
次に、文字列を数値へ変換します。
const L = Number(input1);
const u = Number(input2[0]);
const a = Number(input2[1]);
const b = Number(input2[2]);
const v = Number(input3);
最後に、それぞれへ1を足して出力します。
console.log(L + 1);
console.log(u + 1);
console.log(a + 1);
console.log(b + 1);
console.log(v + 1);
console.log()を5回使うことで、5行に分けて出力できます。
別の書き方
今回の問題は、ほかの方法でも書けます。
書き方1:変数へ直接変換する
最初に書いたコードでは、input1やinput3へ一度保存してから、数値に変換していました。
const input1 = lines[0];
const L = Number(input1);
これを1行にまとめることもできます。
process.stdin.resume();
process.stdin.setEncoding("utf8");
var lines = [];
var reader = require("readline").createInterface({
input: process.stdin,
output: process.stdout,
});
reader.on("line", (line) => {
lines.push(line);
});
reader.on("close", () => {
const L = Number(lines[0]);
const input2 = lines[1].split(" ");
const u = Number(input2[0]);
const a = Number(input2[1]);
const b = Number(input2[2]);
const v = Number(lines[2]);
console.log(L + 1);
console.log(u + 1);
console.log(a + 1);
console.log(b + 1);
console.log(v + 1);
});
こちらは、input1とinput3という変数を作らず、直接Number()で変換しています。
const L = Number(lines[0]);
const v = Number(lines[2]);
元のコードより少し短く書けます。
書き方2:2行目を最初から数値の配列にする
split(" ")で分けた後、map(Number)を使うと、配列の中身をまとめて数値へ変換できます。
const input2 = lines[1].split(" ").map(Number);
この処理をすると、配列の中身は文字列ではなく数値になります。
[8, 0, 2]
コード全体は次のようになります。
process.stdin.resume();
process.stdin.setEncoding("utf8");
var lines = [];
var reader = require("readline").createInterface({
input: process.stdin,
output: process.stdout,
});
reader.on("line", (line) => {
lines.push(line);
});
reader.on("close", () => {
const L = Number(lines[0]);
const input2 = lines[1].split(" ").map(Number);
const v = Number(lines[2]);
const u = input2[0];
const a = input2[1];
const b = input2[2];
console.log(L + 1);
console.log(u + 1);
console.log(a + 1);
console.log(b + 1);
console.log(v + 1);
});
map(Number)とは
次のコードでは、半角スペースで文字列を分割しています。
lines[1].split(" ");
結果は文字列の配列です。
["8", "0", "2"]
さらにmap(Number)を付けると、すべての要素を数値へ変換できます。
lines[1].split(" ").map(Number);
結果は次のようになります。
[8, 0, 2]
ただし、map()にまだ慣れていない場合は、最初に書いたように1つずつNumber()を使うほうが処理を理解しやすいと思います。
書き方3:すべての空白でまとめて分割する
今回の入力は、改行と半角スペースで区切られています。
7
8 0 2
1
実は、改行と半角スペースをどちらも「空白」と考えて、すべてまとめて分割する方法もあります。
process.stdin.resume();
process.stdin.setEncoding("utf8");
var input = "";
process.stdin.on("data", (chunk) => {
input += chunk;
});
process.stdin.on("end", () => {
const numbers = input.trim().split(/\s+/).map(Number);
const L = numbers[0];
const u = numbers[1];
const a = numbers[2];
const b = numbers[3];
const v = numbers[4];
console.log(L + 1);
console.log(u + 1);
console.log(a + 1);
console.log(b + 1);
console.log(v + 1);
});
次の部分で、改行や半角スペースをまとめて区切っています。
input.trim().split(/\s+/);
入力が次の場合、
7
8 0 2
1
分割後は次の配列になります。
["7", "8", "0", "2", "1"]
さらにmap(Number)で数値へ変換すると、次のようになります。
[7, 8, 0, 2, 1]
この方法では、何行目に入力されているかを意識せず、入力された順番で値を取り出せます。
ただし、/\s+/という正規表現が登場するため、初心者のうちは少し分かりにくい書き方かもしれません。
書き方4:分割した値をそのまま使う
変数をたくさん作らず、配列の値を直接使うこともできます。
process.stdin.resume();
process.stdin.setEncoding("utf8");
var lines = [];
var reader = require("readline").createInterface({
input: process.stdin,
output: process.stdout,
});
reader.on("line", (line) => {
lines.push(line);
});
reader.on("close", () => {
const input2 = lines[1].split(" ");
console.log(Number(lines[0]) + 1);
console.log(Number(input2[0]) + 1);
console.log(Number(input2[1]) + 1);
console.log(Number(input2[2]) + 1);
console.log(Number(lines[2]) + 1);
});
短く書けますが、Lやuなどの変数名がないため、どの値を扱っているか少し分かりにくくなります。
学習中は、次のように意味のある変数名を付ける書き方のほうが読みやすいと思います。
const L = Number(lines[0]);
const u = Number(input2[0]);
初心者にはどの書き方がよいか
今回の問題では、最初に書いたコードが一番分かりやすいと感じました。
const input1 = lines[0];
const input2 = lines[1].split(" ");
const input3 = lines[2];
const L = Number(input1);
const u = Number(input2[0]);
const a = Number(input2[1]);
const b = Number(input2[2]);
const v = Number(input3);
少し行数は多いですが、次の処理が順番に確認できます。
- 入力された行を取り出す
- 半角スペースで分割する
- 文字列を数値に変換する
- 計算して出力する
慣れてきたら、map(Number)や空白でまとめて分割する方法を使うと、より短く書けます。
まとめ
今回のポイントは、入力される場所によって値の取り出し方を変えることです。
1行目と3行目は、整数が1つだけなので、そのまま取り出します。
const input1 = lines[0];
const input3 = lines[2];
2行目には整数が3つあるため、split(" ")で分割します。
const input2 = lines[1].split(" ");
また、標準入力から受け取った値は文字列なので、計算する前にNumber()で数値へ変換します。
const u = Number(input2[0]);
今回のように改行区切りと半角スペース区切りが混ざっていても、まずlinesの中に何が入っているかを確認すると、処理を考えやすくなりました。