JavaScriptでプログラミングしていて、次のような処理をコーディングする必要が出てきた。
「二つの配列 Question[ ] と Time[ ] がある。
Question[i]の回答に要した時間がTime[i]に格納されている。
Time[ ]を昇順にソートし、その結果を元にQuestion[ ]も並び替える」
配列が二つなら、「適当なセパレーターで各要素を結合したワーク配列をソートして、string.splitで分割」といったやり方もある。
ただし、セパレーターを選ぶ際に両方の配列の値を考慮する必要がある。
そんなわけで、三つ以上の配列にも対応でき、セパレーターを選ぶのがあまり難しくないやり方を考えた。
/* 最初の引数を並び替える。その結果に従って2番目以降の引数も並び替える */
var sortTwoArr = function(){
var sepstr = "::##&&::"; // 配列の値と添え字をつなげる文字列。最初の引数の配列の値としてあり得ない文字列にする
var arglen = arguments.length; // 引数の数
// 引数チェック
var arrSize; // 配列のサイズ
if(arguments[0] instanceof Array){
arrSize = arguments[0].length;
}
for(var i=0;i<arglen;i++){
if(!(arguments[i] instanceof Array)){
alert((i+1) + "番目の引数が配列ではありません");
return false;
}
if(arguments[i].length != arrSize){
alert((i+1) + "番目の引数のサイズが、最初の引数のサイズと異なります");
return false;
}
}
// 最初の引数に配列の添え字を付加してソート
var workArr = new Array(arrSize); // ワーク配列
var sortSeqArr = new Array(arrSize); // ソート後の添え字を格納する配列
var firstArr = arguments[0]; // 最初の引数
for(var i=0;i<arrSize;i++){
workArr[i] = String(firstArr[i]) + sepstr + String(i);
}
workArr.sort();
// ソート後の添え字を格納
var tempspArr = new Array(2); // string.split用の配列
for(var i=0;i<workArr.length;i++){
tempspArr = workArr[i].split(sepstr,2);
sortSeqArr[i] = tempspArr[1];
}
// returnするオブジェクト作成
var rtnobj = new Array(arglen); // returnするオブジェクト
for(var i=0;i<arglen;i++){
rtnobj[i]=new Array(arrSize);
}
for(var i=0;i<arrSize;i++){
for(var j=0;j<arglen;j++){
rtnobj[j][i] = arguments[j][ sortSeqArr[i] ];
}
}
return rtnobj;
}
Argumentsオブジェクトを使用して、可変数の引数に対応している。
配列は参照渡しだからreturnしなくていいと思っていたが、関数内で値を変えても呼び出し元では反映されなかった。そのためソート済み配列を格納した配列をreturnするようにした。
sortTwoArr関数の使用例は以下の通り。
// ソート対象配列
var firstArr = ["023","100","001","010","880","050"];
var followArr = ["aaa","bbb","ccc","ddd","eee","fff"];
var followArr2 = ["あ","い","う","え","お","か"];
// firstArrをソートした結果に従って、followArr, followArr2をソート
var rtn = sortTwoArr(firstArr,followArr,followArr2);
// ソートした結果を格納
var firstArrAfter = rtn[0];
var followArrAfter = rtn[1];
var followArrAfter2 = rtn[2];
この結果は
firstArrAfter: [001,010,023,050,100,880]
followArrAfter: [ccc,ddd,aaa,fff,bbb,eee]
followArrAfter2: [う,え,あ,か,い,お]
となる。
array.sort()メソッドは、
値を文字列としてソートしているようなので、配列の値を数値としてソートする場合は以下のようにする。
// 関数の外で宣言
var sepstr = "::##&&::"; // 配列の値と添え字をつなげる文字列。配列の値としてあり得ない文字列にする
/* 最初の引数を並び替える。その結果に従って2番目以降の引数も並び替える */
var sortTwoArr = function(){
var arglen = arguments.length; // 引数の数
(略)
workArr.sort(sortfunc);
(略)
return rtnobj;
}
/* 数値比較する場合の比較関数 */
var sortfunc = function(first,second){
var tempspArr = new Array(2); // string.split用の配列
var firstval,secondval;
// 比較対象の値を抽出
tempspArr = first.split(sepstr,2);
firstval = Number(tempspArr[0]);
tempspArr = second.split(sepstr,2);
secondval = Number(tempspArr[0]);
if(firstval > secondval){
return 1;
}else if(firstval < secondval){
return -1;
}
return 0;
}
上記は、配列内に数値と文字列が混在しているケースを考慮していない。
配列のサイズが大きい場合は比較関数の負荷が気になる。
配列の値が数値でも、ゼロ埋めなどで桁数を揃えて文字列として格納すれば、比較関数の使用を回避できる。