1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

今回は paiza のクエリで「Vtuber」の問題に挑戦!

少し前に、宝鐘マリンさんがチャンネル登録者数が400万人突破してて、すごい人気ですよね!


問題概要

あなたはVtuberとして活動中で、配信のたびに視聴者から「superchat」と「メンバーシップ加入」の履歴がある。


  • superchat

    視聴者が配信者に送った「応援金」の合計額を表す。
    1人のアカウントが複数回送ることもあるので、合計額を管理する必要がある。


  • membership

    視聴者がメンバーシップ制度に加入したことを示す。
    加入者リストを管理する。


読み上げる順番は以下の通り:

  • superchatを送ったアカウントを「合計金額の高い順」に読み上げる。
    • 同じ合計金額の場合は「名前を辞書順降順(Z→A)」に並べる。
  • その後、メンバーシップ加入者を「名前の辞書順昇順(A→Z)」で読み上げる。

入力として、イベント数とイベント内容(superchat送金またはメンバーシップ加入)が与えられる。

出力は読み上げ順にアカウント名を一行ずつ表示する。



入力例:

5
aiueo give 2489 !
kk join membership!
coffee_addiction join membership!
so_cute give 837 !
yoyo give 9284 !

出力例:

yoyo
aiueo
so_cute
coffee_addiction
kk






❌ NG例:

const rl = require('readline').createInterface({input:process.stdin});

const lines = [];

rl.on('line', (input) => lines.push(input));


rl.on('close', () => {
    const N = Number(lines[0]);
    
    const superChat = new Map();
    const memberShip = [];
    
    lines.slice(1).forEach(line => {
        const tokens = line.split(' ');
        const name = tokens[0];
        const event = tokens[1];
        
        if (event === 'give'){
            const money = Number(tokens[2]);
            superChat.set(name, superChat.get(name) || 0 + money);
        }
        else if (event === 'join'){
            memberShip.push(name);
        }
    });
    
    const sorted = Array.from(superChat.keys()).sort((a, b) => {
        const A = superChat.get(a);
        const B = superChat.get(b);
        
        return A !== B ? B - A : a - b; 
    });
    
    sorted.forEach(s => console.log(s));
    console.log(memberShip.sort().join('\n'));
});
  • ❌ superChat.get(name) || 0 + money
  • → ✅ (superChat.get(name) || 0) + money
  • 同じ金額だった場合の辞書順ソートが正しくできていない
    (a と b は文字列なので a – b は NaN になる)




✅ OK例:

const rl = require('readline').createInterface({ input: process.stdin });

const lines = [];

rl.on('line', (input) => lines.push(input));

rl.on('close', () => {
    const N = Number(lines[0]); // イベントの数
    
    const superChat = new Map(); // superchat 金額をアカウント名で管理
    const memberShip = [];       // メンバーシップ加入者のリスト
    
    // 2行目以降のイベントを処理
    lines.slice(1).forEach(line => {
        const tokens = line.split(' ');
        const name = tokens[0];
        const event = tokens[1];
        
        if (event === 'give') {
            const money = Number(tokens[2]);

            // すでに superchat 済みなら加算、なければ 0 から加算
            superChat.set(name, (superChat.get(name) || 0) + money);
        } 
        else if (event === 'join') {
            memberShip.push(name);
        }
    });
    
    // superchat 金額が高い順 → 同額は名前を辞書順降順
    const sorted = Array.from(superChat.keys()).sort((a, b) => {
        const A = superChat.get(a);
        const B = superChat.get(b);
        return A !== B ? B - A : b.localeCompare(a);
    });

    
    // superchat 順に出力
    sorted.forEach(s => console.log(s));

    // メンバーシップは辞書順昇順に出力
    memberShip.sort().forEach(m => console.log(m));
});

  • Map で superchat 金額を効率管理。(名前がキー、総額が値)
  • superchat のソートは「金額の降順」→「金額同じなら名前の辞書順降順」
    • A !== B ? B - A : b.localeCompare(a)
  • memberShip.sort() → デフォルトで辞書順昇順。




💡localeCompare() メソッド

localeCompare() メソッドは、参照文字列がソート順で指定された文字列の前か後か、または同じかを示す数値を返す。


🔍基本構文

"a".localeCompare("b")

これは "a" と "b" を 辞書順で比較するメソッド。


戻り値は:

・負の数 → 参照文字列(左の “a”)の方が 前。

・0 → 同じ。

・正の数 → 参照文字列(左の “a”)の方が 後。


👉 つまり:

"apple".localeCompare("banana") // 負の数(appleが前)
"banana".localeCompare("apple") // 正の数(bananaが後)



🔍Array.prototype.sort() のコールバックでの順番

array.sort((a, b) => {
  return a.localeCompare(b);
});

sort の比較関数は ab の 2 つの要素を受け取り:

・戻り値が負の数 → a が b より前に来る

・戻り値が 0 → 順序を変えない

・戻り値が正の数 → a が b より後に来る


👉つまり、localeComparesort で使うと

array.sort((a, b) => a.localeCompare(b));

→ 辞書順 昇順(A → Z)


例えば:

["banana", "apple"].sort((a, b) => a.localeCompare(b))
// → ["apple", "banana"]



🔍今回の問題の場合

superchat の金額が同じ場合、同じ金額の中で辞書順降順でアカウント名を読むことにしました。


降順にしたいときは順番を逆にすれば OK!

array.sort((a, b) => b.localeCompare(a));

👉 b.localeCompare(a) にすると:

ba より前 → 正の数 → a が後ろ → 「降順」。





🗒️ 学習メモ・まとめ

  • Map が便利
    • キーにアカウント名、値に合計金額を格納。
    • Map.get(name) || 0 で初期値を扱う。

  • sort の比較関数の理解が重要
    • 数値の降順は b - a
    • 文字列の辞書順昇順は a.localeCompare(b)
    • 文字列の辞書順降順は b.localeCompare(a)

  • localeCompare() メソッドで辞書順ソートができる
    • 言語やロケールに対応した正確な文字列比較が可能。
    • a.localeCompare(b)ab の前なら負の値、後なら正の値を返す。

  • 条件によって複数のソート基準を使い分ける
    • 今回は「superchat金額の降順」→「金額同じなら名前の辞書降順」
    • 「メンバーシップ加入者は名前の辞書昇順」

  • 文字列に対して - 演算子は使えない
    • a - bNaN になるため、文字列比較は必ず localeCompare を使う。

  • 入力の解析とデータ構造の選択
    • 入力を分解し必要な情報を効率よく格納・集計すること。
    • 配列・Mapを状況に応じて使い分ける。

  • 出力は問題の指示に忠実に
    • 指定された順序・形式で出力すること。




僕の失敗談(´;ω;`)と解決法🐈

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?