LoginSignup
Pocketroppo
@Pocketroppo

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

【初心者】Paiza問題集「辞書データの順序」の解答例に対する疑問

解決したいこと

Paiza問題集「辞書データの順序」の解答例(JavaScript)に疑問があるため、詳しい方に解説をお願いしたいです。

解答例

const fs = require("fs");

const input = fs.readFileSync("/dev/stdin", "utf-8").trim();

const lines = input.split("\n");
const n = Number(lines[0]);
const m = Number(lines[n + 1]);

const dict = new Map();
const array = [];
for (let i = 1; i <= n; i++) {
  array.push(lines[i]);
  dict.set(lines[i], 0);
}
for (let i = n + 2; i < n + m + 2; i++) {
  const p = lines[i].split(" ")[0];
  const a = Number(lines[i].split(" ")[1]);
  if (dict.get(p)) {
    dict.set(p, dict.get(p) + a);
  } else {
    dict.set(p, a);
  }
}

array.sort();
array.forEach((name) => {
  console.log(dict.get(name));
});

わからないこと

①以下の部分の if (dict.get(p)) が何を意味しているかがわかりません。
攻撃の情報(lines[i])の中に、攻撃を受けるプレイヤーの名前が入っていればダメージ値を追加するということでしょうか?
dict.get(p)でプレイヤーの初期ダメージ値を得る意図が理解できていません。

if (dict.get(p)) {
    dict.set(p, dict.get(p) + a);
  } else {
    dict.set(p, a);
  }

②以下の部分について、arrayをアルファベット順にソートして、forEach()メソッドを使い、各プレイヤーの名前に対応するダメージ値を取得し、出力していることはなんとなくわかりますが、forEachの引数のnameが具体的に何を指しているのかがわかりません。

array.forEach((name) => {
  console.log(dict.get(name));
});
0

4Answer

既に回答されていますが、追加質問について回答します。

else以降は、名前のリストには存在しないのにダメージのリストにだけ存在する場合や、戻り値がundefinedになった場合に、dictに新しくエントリを作るという理解でしょうか。

その通りです。
もしも、分岐処理をせず、「名前のリストには存在しないのにダメージのリストにだけ存在する場合」は dict.get(p) の返り値が undefined になります。
undefined に数値を加算すると評価値が NaN になるので、分岐処理させてNaN値になるのを防いでいるのでしょう。

個人的には、ダメージ計算リストと名前リストの処理順を逆にするのがわかりやすくてよいと思います。

  1. ダメージ計算リストを dict に格納
  2. 名前リストにあり、dict にない場合、0で初期化

なお、キーに対応する要素の存在判定をする場合、Map.prototype.has() を使用するのが妥当です。

if (dict.has(p)) {
  dict.set(p, 0)
}

②以下の部分について、arrayをアルファベット順にソートして、forEach()メソッドを使い、各プレイヤーの名前に対応するダメージ値を取得し、出力していることはなんとなくわかりますが、forEachの引数のnameが具体的に何を指しているのかがわかりません。

この質問に関しては、consoleやbreakpointで変数 name の値を確認すればすぐにわかることなので、自分で確認する癖をつけると良いと思いました。

console.log(name);
1

なんともわかりにくい解答例ですね・・・

回答前に前提を説明します。

const dict = new Map();
const array = [];
for (let i = 1; i <= n; i++) {
  array.push(lines[i]);
  dict.set(lines[i], 0);
}

この部分は名前を取得している部分です。
「2 行目から (n + 1) 行目には人の名前 s_1, ..., s_n が改行区切りで与えられます」の部分ですね。
なので、変数arrayは名前のリストです。
そして変数dictはMapオブジェクトで、名前と受けたダメージのペアです。ダメージの初期値は0に設定されています。

① 以下の部分の if (dict.get(p)) が何を意味しているかがわかりません。

まず、名前 受けたダメージの行が分解されて、それぞれ変数pに名前、変数aに受けたダメージが入ります。
dict.get(p)は名前が受けたダメージを取得しています。
それをifで判定する意味は、名前が受けたダメージが0(初期値)かどうかの判定です。(数値の0はfalseと判定されます)

② forEachの引数のnameが具体的に何を指しているのかがわかりません。

最初の解説のとおり変数arrayは名前のリストです。配列をforEachすると値が順に取得されます。
つまり変数nameは名前です。

0

Comments

  1. 訂正:プレイヤー名 => 名前

    問題文に合わせた名称に直しました。

①以下の部分の if (dict.get(p)) が何を意味しているかがわかりません。

@Blue32aさんは「キーpの値が0か否かの判定」と書かれていますが、Map.getは存在しないキーを指定した場合にundefinedを返すので「dictにキーがpのエントリが含まれるかの判定」とも解釈できます。

@Blue32aさんが解説されていますが、全てのプレイヤーの蓄積ダメージはdictに0で初期化されています。
なので実際には「0か否か」の判定で動いていますが、単純にif/else不要の

dict.set(p, dict.get(p) + a);

だけで問題ないのです。
なので意図がわからない条件式とも言えます。

0

Comments

  1. @Pocketroppo

    Questioner
    全ての名前の初期ダメージ値は0に設定されているはずなのに、なぜif文で取得するのだろうと疑問に思っていましたが、キーがpのエントリが存在するかどうかを判定すると考えると少し納得できます。
    お二人のご回答で何となく理解できた気がします。どうもありがとうございます。

@mrbonjin
補足説明ありがとうございます。
確かにMap.getメソッドの戻り値だけみればundefinedがあるので、そのチェックが目的なのかもしれません。

そうなると「名前のリストには登場しないがダメージのリストには名前がある」というケースを想定しているのでモヤッとしますが・・・たぶんそこまで踏み込まず「Map.getメソッドの戻り値にはundefinedになるケースがあるので、それをチェックしている」というざっくりした感じでいいのでしょう。

0

Comments

  1. @Pocketroppo

    Questioner
    詳しくご解説いただきありがとうございます。
    つまり、dictにキーがpのエントリが存在するかを確認し、存在するのであればダメージ値を追加するということですね。
    else以降は、名前のリストには存在しないのにダメージのリストにだけ存在する場合や、戻り値がundefinedになった場合に、dictに新しくエントリを作るという理解でしょうか。
  2. > 名前のリストには存在しないのにダメージのリストにだけ存在する場合

    これが戻り値がundefinedになるケースですね。その場合にはdictに新しく追加されます。
    ただし最後は名前リストにそって出力しているので、追加しても表示されないのですが・・・

    さらに前述の通り、0の場合もelseの処理になるので、この場合は更新になります。

    > set() メソッドは、指定されたキーと値を持つ要素を Map オブジェクトに追加したり、更新したりします。

    https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Map/set

Your answer might help someone💌