|
連想配列、多重配列のソート関数ライブラリ Hash |
|
|
連想配列はそのままではソートができないので、普通の配列の親を作ってその子供(プロパティ)化けてソートします。
、同じような式を使う物なので、使っていた物を掲載しておきます。
DOM時代に入ってからはXMLも含めてオブジェクトを扱う事が非常に多いので、コアのArrayオブジェクトに用意されていても良さそうな物ですが・・・・。
1次元のキー&バリューのペアだけでなく連想配列が多重(多次元)配列の場合でも、
特定のプロパティを指定して、それを基準にソートする事も可能です。
ベタな関数なので、もっと良い物があればそっちを使ってください。
連想配列のソート方法は ==> チップス メモ 連想配列のソートの仕方を参照。
ソースファイル |
|
ソースはエラーチェックなしの物と、エラーチェックありの物があります。
エラーチェックで倍のデータ量になるので両方置きました。どちらでも使いやすい方を。
(ちなみに私は完成までエラーチェックあり、完成後に置き換えています。というか部分使用ですが・・・)
hash.js(エラーチェックあり) と hash_no_error.js(エラーチェックあり) が入っています。
WindowsのShift_JIS記述です。
使い方 |
|
■基本情報
var ステートメントでのオブジェクトにしてあるので、スクリプトをスクリプトの冒頭に記述して読み込ませるか、
使用するスクリプトよりも先に外部ソースファイルとして読み込ませてください。
関数内に記述してローカル変数(オブジェクト)として読み込ませる事もできます。
変数名はHash というオブジェクト名しかグローバルでは使っていませんので、他のプログラムと干渉しにくいはずです。
書き出しのvar Hash = の部分
Hashと言う名前が他の変数や関数名とぶつかる場合は別の名前に変更可能です。
■
Hashオブジェクト 簡単な説明
次のようなメソッドを持たせてあります。(詳しい解説は後半)
kvsort [ 戻り値:ソート後の連想配列 ]
変数 = Hash.kvsort( オブジェクト [,比較対象 [,スイッチ(true|false)]] )
例:Array = Hash.kvsort(Array,'key')
比較対象は 'key'(キーソート)、'val'(バリューソート)、'特定位置のvalue'(多重配列用比較要素ソート)、の方法があります。
reverse [ 戻り値:リバース後の連想配列 ]
変数 = Hash.reverse( オブジェクト [,スイッチ(true|false)] )
例:Array = Hash.reverse(Array)
key & valueのペアを保持したまま正反対の並び順に並べ替えます。
grep [ 戻り値:マッチした物だけを集めた配列 ]
変数 = Hash.grep(オブジェクト ,match()用パターン [,type])
例:grepary = Hash.grep(Array,/^regs/,'val')
typeは与えられたオブジェクトの'key'か'val'の対象を指定。
length [ 戻り値:連想配列の長さ(数値型) ]
変数 = Hash.length(連想配列)
例:len = Hash.length(Array)
空要素まで含めた長さを示す数が数値型で戻ります。
list [ 戻り値:連想配列のキーリスト、バリューリストの配列 ]
変数 = Hash.list(オブジェクト [,type])
例:keys = Hash.list(Array,'key')
例:values = Hash.list(Array,'val')
キーだけ、要素だけ、の配列(indexを持つ普通の配列)を返してきます。
スイッチは対象として 'key' か 'val' だけです。
toIndextype [ 戻り値:index型新配列のkeyとvalプロパティになった配列 ]
変数 = Hash.toIndextype(連想配列)
例:nArray = Hash.toIndextype(Array)
AAA['xxxx'] = 'zzzz';が BBBB[0].key = 'xxxx'; BBBB[0].val = 'zzzz'
のようにindexを持つBBBBという新しい親配列のオブジェクト要素 key(固定プロパティ名)とval(固定プロパティ名)になったもの。
基本的に内部用関数で、外からも一応使えるようにしてあるだけです。
toHashtype [ 戻り値:toIndextypeの逆 ]
変数 = Hash.toHashtype(2重配列[,スイッチ])
例:nArray = Hash.toHashtype(Array)
toIndextypeで作られた配列、または key(固定名)とval(固定名)という2つのプロパティだけを持つオブジェクトを
AAAA[key] = val; という単純な連想配列に戻します。
BBBB[0].key = 'xxxx'; BBBB[0].val = 'zzzz'; を AAA['xxxx'] = 'zzzz';にした配列を返します。
toIndextypeの逆処理です。
コアのArrayのメソッドの様に Array.sort()とするだけで処理が済む物ではなく、
Array = Hash.property(Array);とする事で自分に返す、または新しい配列を作る形で使います。
Arrayやオブジェクトにprototypeでメソッドを埋め込むと、連想配列のキーと干渉するのでこうしてあります。
オブジェクト式の記述がわかる方は、Hashを H にしてkvsortを s の様に書き換えてしまえば H.s()の様に縮めて使えます。
詳細説明 |
|
■
kvsort
kvsort [ 戻り値:ソート後の連想配列 ]
変数 = Hash.kvsort( オブジェクト [,比較対象 [,スイッチ(true|false)]] )
◆引数の対象要素
『空』または 'val' → オブジェクトのバリューソート。
'key' → キーソート。
戻り値を格納する変数を引数のオブジェクトと同じにすれば配列がソートされて自分自身に戻ります。
別変数にすればソート後の別の新配列(オブジェクト)となります。
スイッチを設定する場合は、比較対象指定は完全省略はできません。
(スイッチに関しては後述)
bTest = Hash.kvsort(bTest,false) × falseが比較対象と間違われ駄目
bTest = Hash.kvsort(bTest,,false) × これはJavaScriptエラー
bTest = Hash.kvsort(bTest,'',false) ○ これは空を渡すので大丈夫。
bTest = Hash.kvsort(bTest) ○ これも残りの引数省略で大丈夫
◆多重配列関連
多重配列で特定の深度の部分が配列・オブジェクトである場合で
そこをソートする場合は
bTest['aaa']['ccc'] = Hash.kvsort(bTest['aaa']['ccc'],'key');
の様にそのオブジェクトを指定する事で、その部分だけがソートされます。
元のオブジェクトに戻せば、多重配列のその部分だけがソート済になります。
bTest.aaa.ccc = Hash.kvsort(bTest.aaa.cccc,'key');
でもJavaScriptのオブジェクト・プロパティ記述に沿うので当然同じ意味です。
多重配列(連想配列を含む)の場合は比較対象をそのオブジェクトのキー、バリューソートだけではなく、
ある特定の深度の特定のバリューを比較対象として上層の配列をソートする事ができます。
(例えば、曾孫の aaaと言うキーの値の大きい順に全体を並べ替える 等)
こういう場合は比較要素にその比較する対象のバリューを指定してください。
bTest['gen2'] = Hash.kvsort(bTest['gen2'],'["cat2"]["female"]');
これは gen2の下位にオブジェクトを要素として持つ配列があり、さらにその下層の["cat2"]["female"]が持つ固定値を参考に並べ替えると言う事になります。
この場合gen2の直下にあるオブジェクトは全てその下層に["cat2"]["female"]というオブジェクトを持っていなければソートは無理です。
つまりbTestと言う多重配列は
bTest['gen2'][0]["cat2"]["female"]
と表現できる値を持っていると言う事です。
この 0の層の配列をソートしてる事になります。
bTest[上層][ココが並べ替え][下層][下層]の様な場合は
bTest[上層] = Hash.kvsort(bTest[上層],'[下層][下層]');
と言う指定方法になり、並べ替える場所は当然固定できないので指定には含まれないので使い方に注意。
これは配列の構造が頭に入っていないと指定が難しいケースが多いです。
うちで配布してる連想・多重配列のテーブルビュアーやツリービュアーなどで構造確認してください。
注意:
この対象指定方法やバリューソートの場合は、比較対象がオブジェクトだとソートする度に順序が入れ替わる可能性があります。
これはバリューが『値』ではなくオブジェクトなので、JavaScriptが『 object 』と言う文字を返すので判断ができない為です。
◆スイッチについて
スイッチは、バリューソートで普通の配列が紛れ込んでいる時に基本的に使います。
多重配列の場合、特定の深度が連想配列・オブジェクト以外の普通の配列になっている場合もあります。
こういった場所をソートする時に、そのソート対象が配列なのかそれとも連想配列・オブジェクトなのかに関しては、
コンストラクタとキーを調べて自動判定をしています。
が、JavaScriptでは 連想配列であってもキーが数値の場合は普通の配列と判断が付かない為、
自動判定ではキーが全て数値のみのでコンストラクタがArrayの連想配列は普通配列として扱ってしまいます。
こういった場合に明示的に
ary = Hash.kvsort(ary,'',true)
とスイッチを指定する事で、この混乱を避ける事が出来ます。
true 明示的に連想配列として扱う指示
false 明示的に普通配列として扱う指示
元々連想配列が作られる時点できちんと ary = new Object()
または、a = {} として作られた場合は、キーが数値であってもコンストラクタがObjectなので、
確実にオブジェクトとして扱われるのでスイッチの必要はありません。
ネットをあちこち見てると、コンストラクタがArrayの場合の連想配列と、形の上から連想配列とも呼べるオブジェクトが、
全く同じ物の様に扱われてるので、このスイッチを付けました。
コンストラクタの違いと、数値のみのキーのケースの扱いが違う事以外は、見た目は同じですからね。
この余計なスイッチを使わない為には、キーが数値のみの場合に限らず
連想配列はコンストラクタArrayでは作らない癖をつける必要があります。
参照 => 配列のソート
余談:
キーに数値以外を持つオブジェクトにfalseのスイッチを付けてしまった場合は、キー&バリューのペアの保持が壊れます。
普通配列なのにtrueを指定した場合は、これは普通配列ではなくなり キーの数値&バリューのペアが固定化され保持されるオブジェクトに生まれ変わります。
これは利用可能です。
キーソートの時はこのスイッチは何を付けても無意味です。
キーソートとは連想配列に行う処理ですから、必ずtureが設定されている物として扱われます。
動作サンプル(結果を視覚化してます)
例1:Objet[0]の下層配列の持つcat2〜gr2のキーをソート
Object[0] = Hash.kvsort(Object[0],'key');
例2:Objet[0]の下層配列cat2〜gr2を、それぞれが持つ孫のfemaleの値でソート
Object[0] = Hash.kvsort(Object[0],'["female"]');
■
reverse
reverse [ 戻り値:リバース後の連想配列 ]
変数 = Hash.reverse( オブジェクト [,スイッチ(true|false)] )
連想配列をリバース。連想配列やオブジェクトの場合は単純なリバースです。
連想配列も一応定義された順に並んでいるので、キー&要素のペアが保持された状態でリバースされます。
多重配列の特定の階層をリバースをする場合はその場所を指定
bTest['ddd'] = Hash.reverse(bTest['ddd'])
bTest.ddd = Hash.reverse(bTest.ddd)
普通の配列か連想配列・オブジェクトであるかどうかは自動的に認識します。
kvsortと同じで、キーが数値のみの連想配列の場合は、オブジェクトである事を指定する必要があります。
スイッチはその為の true / false です。
true 明示的に連想配列として扱う指示
false 明示的に普通配列として扱う指示
kvsortの説明を読んでください。
基本は省略できますが、コンストラクタによっては混同されるのでそれを防ぐ為のスイッチです。
正確に作られている配列・オブジェクトの構成であればスイッチは不要です。
例1:Objetをリバース
Object = Hash.reverse(Object);
このオブジェクトの持つキー 0 と 1 は可変インデックス(普通配列)なので、キーは変らずそのバリューである下層の値が入れ替わります。
例2:Objet[1]を指定してその下cat3〜cat1をリバース
Object[1] = Hash.kvsort(Object[1]);
これはオブジェクトですのでキー部分とその値(下層)が一緒にリバースされます。
■
grep
grep [ 戻り値:マッチした物だけを集めた配列 ]
変数 = Hash.grep(オブジェクト ,match()用パターン [,type])
オブジェクト、パターンは必須項目です。
typeは省略した場合と'val'はバリューのマッチングを行い、
'key'を指定するとキーのリストでマッチングを行います。
match用パターンは、文字列や変数、/^aaa/、の様なmatchで使える物です。
省略した場合はエラーは出ませんが、元の配列が戻るだけです。
与えられたオブジェクトのキーとバリューを調べてパターン(RegExp、文字列(文字・数値など))にマッチする物だけ取り出します。
grepは与えられた配列の中からマッチする物を探すので、引数のオブジェクトが固定要素などを指す物であった場合は対象外となります。
バリューを調べる場合は、引数のオブジェクトの指す配列の要素が更にオブジェクトであった場合は当然文字列ではないのでマッチしません。
が、更に下層にオブジェクトがあるかどうかを grep したい場合の為に、マッチパターンを『 /O-B-J-E-C-T/ 』とすると、
文字列や数値ではなく typeofの結果がobjectの物をピックアップするようになています。
従って、文字列検索で『 O-B-J-E-C-T 』と言う文字列検索ができない事を同時に意味します。
戻り値の配列には、その指定した深さ(オブジェクト)の下層のみが入ります。(相対的要素)
部分切り出しで新たな配列にする物なので、親の深度情報は当然含まれません。
大元の配列からgrepでマッチする物だけを取り出すと言うやり方であれば、
『指定深度』 +『戻り値の配列の要素』 が大元の多重配列の中での正確な位置情報となります。
注意:
bTest = Hash.grep(bTest.gen2[0],/cat/,'key') とした場合は
catはgen2[0]の要素に見えるが、gen2[0]の要素であるオブジェクトの中のキーの1つでしかない。
従って指定はvalではなく当然keyでgrepしなければ駄目。
例1:Objet[0]からキーがcatの文字を持つ物をgrep
var greped = Hash.grep(Object[0],/cat/,'key');
■
list
list [ 戻り値:連想配列のキーリスト、バリューリストの配列 ]
変数 = Hash.list(オブジェクト [,type])
例:
keys = Hash.list(Array,'key')
例:
values = Hash.list(Array,'val')
スイッチは対象として 'key' か 'val' だけです。
例1:Objet[0]のキーリスト、ヴァリューリスト の配列を作る
var keys = Hash.list(Object[0],'key');
キーは文字列なので普通配列で、値が文字列の配列ができます。
var values = Hash.list(Object[0],'val');
この例ではバリューがオブジェクトなので、そのオブジェクトを要素とする普通配列ができます。
■
toIndextype
toIndextype [ 戻り値:index型新配列のkeyとvalプロパティになった配列 ]
変数 = Hash.toIndextype(連想配列)
例:
nArray = Hash.toIndextype(Array)
これは基本的にHashオブジェクトの中の内部関数です。
一応外から使えるようになっているだけです。
連想配列やオブジェクトは arr[key] = value; と言う形になっています。
key指定でvalueを取り出すのには便利ですが、ソートができません。
一旦親配列を作ってその子に入れ込む事でプロパティ参照でsortができます。
oya[0].key = arrのkey;
oya[0].val = arrのarr[key]; // つまりvalueだった物
oya[1].key = arrのkey;
oya[1].val = arrのarr[key]; // つまりvalueだった物
と言う状態になり
oya.sort(function(a,b){ return (a.key > b.key) 1 : -1 })
と言う方法でソートが可能です。
これを元のペア型のオブジェクトに戻せば並び順を変えられます。
参照 => 連想配列のソート
Hash.kvsortは単純に言えば内部でこの順序で処理をしているだけです。
Hash.toIndextypeのみを行うのであれば
oya = new Array();
for( i in arr ){ oya.push( { 'key':i, 'val':arr[i] } ) }
という単純な式ですみますので、わざわざHashオブジェクトを使う必要はないです。
■
toHashtype
toHashtype [ 戻り値:toIndextypeの逆 ]
変数 = Hash.toHashtype(2重配列 [,スイッチ])
例:
nArray = Hash.toHashtype(Array)
Hash.toIndextypeで作った形を、元の連想配列、またはオブジェクトに戻します。
またkey,valと言う二つのプロパティ名を持った配列を ar[key] = val; という連想配列・オブジェクトに戻します。
連想配列というのは、普通配列ともオブジェクトとも微妙に違うニュアンスを持つので、
戻す時にスイッチで 連想配列、オブジェクトどちらかを指定できます。
基本は省略で、オブジェクトになります。
キーが数値でなければ、falseを指定する意味がありませんし、toIndextype前の配列が普通配列であった場合
何故toIndextypeを使ったのか?も意味不明で、普通に Array.sort()できたはずですので、不要なスイッチとも言えますが。
trueまたは省略で new Object();
false だと new Array();
と言うコンストラクタの違う連想配列になります。