FC2ブログ

JavaScriptで、一つの配列をソートした結果に従って他の配列もソートする

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;
}
上記は、配列内に数値と文字列が混在しているケースを考慮していない。
配列のサイズが大きい場合は比較関数の負荷が気になる。
配列の値が数値でも、ゼロ埋めなどで桁数を揃えて文字列として格納すれば、比較関数の使用を回避できる。
スポンサーサイト



コメント

コメントの投稿

非公開コメント

プロフィール

himax64

Author: 南西
30代後半の無職です。
就活もせずダラダラ生きてます。
作ったもの

最新記事
人気記事
検索フォーム
カテゴリ
月別アーカイブ
最新コメント
最新トラックバック
RSSリンクの表示
QRコード
QRコード
カウンター