CodeIQ:「Excelなら簡単なソート」

私自身が表題の問題を解いた時のプログラムについて解説します。
問題の詳細は「Excelなら簡単なソート」CodeIQ)を参照してください。

問題の概要

問題を引用します。

生徒たちが受けたテストの結果を表にまとめました。
表には、英語、国語、数学の点数がそれぞれ記載されています。
これを、英語、国語、数学の順に点数が高い方から並べたいと考えています。
つまり、英語の点数が同じ場合は国語の点数が高い方を上に表示、英語と国語の点数が同じ場合は数学の点数が高い方を上に表示したいです。
※すべての点数が同じ場合は、名前のアルファベットで昇順に表示します。

例えば、次のようなCSV形式のデータが標準入力から与えられた場合、標準出力に以下の内容を出力するプログラムを作成してください。

【Input】
name,english,japanese,math
a,58,29,56
b,64,86,18
c,89,50,36
d,89,50,84
e,95,89,53
f,64,99,43
g,13,59,68
【Output】
name,english,japanese,math
e,95,89,53
d,89,50,84
c,89,50,36
f,64,99,43
b,64,86,18
a,58,29,56
g,13,59,68

私のプログラム

JavaScript(Node.js)で解答しています。

process.stdin.resume();
process.stdin.setEncoding('utf8');

/**
 *	一人分の成績
 */
var Score = (function(){
	function Score(order){
		// 入力値の並び順
		this.Order = order.trim().split(',');

		// パラメータ初期化
		this.Value = {};
		for(var i=0; i<this.Order.length; i++){
			this.Value[this.Order[i]] = 0;
		}
	}

	// 成績をセットする
	Score.prototype.setScore = function(input){
		var item = input.trim().split(',');

		for(var i=0; i<this.Order.length; i++){
			if(this.Order[i] == 'name'){
				this.Value[this.Order[i]] = item[i];
			}
			else{
				this.Value[this.Order[i]] = parseInt(item[i]);
			}
		}
	}

	// 席席を名前、英語、国語、数学の順で出力する
	Score.prototype.print = function(){
		console.log(this.Value.name + ',' + this.Value.english + ',' + this.Value.japanese + ',' + this.Value.math);
	}

	return Score;
})();

// 入力された成績のリスト
var Inputed = Array();

// 成績の順番
var Order = "";

process.stdin.on('data', function (chunk) {
    var lines = chunk.toString().split('\n');

    for(var i=0; i<lines.length; i++) {
		var input = lines[i].trim();
		if(!input){
			continue;
		}

		if(i==0){
			Order = input;
			continue;
		}

		var p = new Score(Order);
		p.setScore(input);
		Inputed.push(p);
    }
});

process.stdin.on('end', function () {
	Inputed.sort(function(a, b){
		if(a.Value.english != b.Value.english){
			return b.Value.english - a.Value.english;
		}

		if(a.Value.japanese != b.Value.japanese){
			return b.Value.japanese - a.Value.japanese;
		}

		if(a.Value.math != b.Value.math){
			return b.Value.math - a.Value.math;
		}

		if(a.Value.name >= b.Value.name){
			return 1;
		}
		else{
			return -1;
		}
	});

	console.log("name,english,japanese,math");
	for(var i=0; i<Inputed.length; i++){
		Inputed[i].print();
	}
});

解説

この問題は仕様通りにプログラミングすればOKなので難しい部分はありません。ソートが使えるかどうかの技術的な確認が目的でしょう。

Scoreクラス

私は1人分のデータを1つのクラスに取るようにしています。プログラムを短くしたければ二次元配列でもできますが、こっちの方が良い(わかりやすい)コードになると思います。
多少の工夫として、入力項目(name,english,japanese,math)をコンストラクタにとって、値は入力項目ごとの連想配列にしている部分です。このようにしておけばテストケースによって項目の順番が変わったり、項目数が増減しても問題ありません。
print()は1人分のデータを出力する関数です。出力順は仕様で指定されているのでベタ書きにしています。

入力値取得

process.stdin.on('data', function (chunk){});で入力値を取得し、1人ごとにScoreのインスタンスを生成してInputed配列に登録します。1行目は項目ラベルなので別あつかいします(56行目)。

ソート

process.stdin.on('end', function(){});でソートして出力します。sortの第二引数の比較関数では仕様通りにenglish、japanese、math、nameの順で比較するだけです。
ソートが完了したら1人ごとに結果を出力するだけです。

雑感

この問題は割と実際的な問題で、仕事で作成するプログラムでもありそうな感じです。ただ、実際の仕事でやる場合はInputedやソートもクラスでラップすべきでしょう。