パソコン工房の蔵出し・アウトレットパソコン

配列のソート、連想配列のキー名などの注意

普通に使うsort()やreverse()はきちんと正しく使っていれば問題は起きません。
が、ちょっと雑なコーディングをすると、ブラウザによって結果が違ってしまう場合などがあります。

また、配列はArrayオブジェクトでオブジェクトではありますが、Objectオブジェクトとは違います。 基本的に連想配列(コンストラクタがArray)とオブジェクト(コンストラクタがObject)は、ほとんど同じ扱いなのですが、 キーを数値にした場合などに注意が必要になります。


例で説明

var bTest = new Array();
bTest[0] = 9;
bTest[1] = 5;
bTest[2] = 4;
bTest[3] = 7;
bTest[4] = 1;

/* リテラルな書き方だと */
var bTest = [9,5,4,7,1];
キーが数値で連番になっている配列。つまり普通の配列です。
これは、リバースもソートも同じく可変indexとなります。
push()などで入れ込んでいくとこんな感じで普通に並んでくれます。

var bTest = new Array();
bTest['0'] = 9;
bTest['1'] = 5;
bTest['2'] = 4;
bTest['3'] = 7;
bTest['4'] = 1;

/* 連想配列ではないのでリテラルな書き方だと */
var bTest = [9,5,4,7,1];
これは文字列としてキーを付けていますがキーは全て数字です。
『可変indexと文字列で指定した数字は意味が違う』と言いたいのですが、 普通の可変indexと同じ動きをします。 つまり同じです。
コンストラクタがArrayであれば、全ての配列要素のキーが数字のみであればそれは可変indexとして認識されてしまいます。

キーが数字でもコンストラクタをObjectにすれば
var bTest = new Object();
bTest[0] = 9;
bTest[1] = 5;
bTest[2] = 4;
bTest[3] = 7;
bTest[4] = 1;

/* リテラルな書き方だと */
var bTest = {0:9,1:5,2:4,3:7,4:1];
とした場合は、オブジェクトなので当然固定。
リバースやソートの配列用のメソッドはもちろん使えない。
これをソートするには連想配列のソート方法を使わないと駄目。

と言う事で、ここまでは似ててもちょっと違う配列とオブジェクト
コンストラクタがArrayかObjectかで見た目が似てても別物です。

var bTest = new Array();
bTest[3] = 7;
bTest[4] = 1;
bTest[5] = 3;
bTest[0] = 9;
bTest[1] = 5;
bTest[2] = 4;

/* こういう場合リテラルな書き方は無い */
手動で何かを指定してソースを書き換えたりしてるうちにこうなる事などがある。 これも普通の配列であり、indexは可変でありリバースやソートはきちんと行われる。

が、問題なのが並び順

これらをソートするとindexは正しい役割をしっかり果たして正しい結果が返る。 index順はソートの結果、リバースの結果をきちんと表している。
でも『物理的?な並び順はこのまま』。キーの並び順と言うか・・・
var bTest = new Array();
bTest[3] = 7;
bTest[4] = 1;
bTest[5] = 3;
bTest[0] = 9;
bTest[1] = 5;
bTest[2] = 4;

bTest.sort();
結果
bTest[3] = 5
bTest[4] = 7
bTest[5] = 9
bTest[0] = 1
bTest[1] = 3
bTest[2] = 4

この通りソートはきちんとされているが、キーの物理的並びが同じ。

bTest[0] = 1
bTest[1] = 3
bTest[2] = 4
bTest[3] = 5
bTest[4] = 7
bTest[5] = 9
こうなってると思い込んでいたが、こうなるのはOperaとSafari。
Opera、Safariはきちんと0から整列したindex通りの順番になる。 それ以外はこの物理的並び順はソートやリバースでも実行する度に変わる事もある。

結果が正しければ物理的な並び順など問題ないと言えるのだが、
これの何が問題かと言うと
for(var i=0; i<bTest.length; i++){ 処理 }
で普通に処理を行えばきちんとindex順に処理がされるし問題は起きない。 が、雑に、連想配列やオブジェクト用の
for( key in bTest){ 処理 }
を行ってしまうと、これはindex順ではなく、物理的並び順で処理が行われる。

Netscpae、FireFoxの場合はbTest = bTest.slice(0)をしてやると Operaと同じように綺麗にindex順の物理的ならびになってくれるが、IEは変らない。
では、この特徴が何かに逆利用できるか?と思ったら利用価値が無い。
ブラウザによって違う結果が返ってくるので使い道が無い。

しかも自動整列の仕様はSafariでは問題はないが、Operaではこれが別の問題点を作っている。(後述)

次は別のケース、更に注意
var bTest = new Array();
bTest[3] = 7;
bTest[4] = 1;
bTest[8] = 3;
bTest[0] = 9;
bTest[1] = 5;
bTest[2] = 4;

これは普通の配列だがindexが飛んでしまってるような配列を作ってしまった場合。
これをsort()するときちんと間を生めて、0からのindexが振られる。 が、前述の物理的並び順は、やはりそのままで間が埋められるので
bTest[3] = 5
bTest[4] = 7
bTest[0] = 1
bTest[1] = 3
bTest[2] = 4
bTest[5] = 9
ま、これも先ほどの注意で for(key in Array)を使わなければ問題は無い。

が、少し問題なのが reverse()

わかり易いように順番は正しくして書きます。
var bTest = new Array();
bTest[1] = 7;
bTest[2] = 1;
bTest[3] = 3;
bTest[5] = 9;
bTest[7] = 5;
bTest[9] = 4;
indexが飛んでる
bTest.reverse()の結果 IE
bTest[2] = 5
bTest[7] = 1
bTest[0] = 4
bTest[8] = 7
bTest[6] = 3
bTest[4] = 9
リバースの結果は一応正しい。
が、OperaとSafariはきちんと並ぶが、IEやNetscape系は物理的並びはめちゃくちゃだしindex番号も違う番号になってるし間が開いたまま。 なので、このリバース処理をした後に
for(var i=0; i<bTest.length; i++){ 処理 }
で、処理をする時は値がundefinedの物を省くような 条件文が必要。
for(key in Array){ 処理 }
で処理をしたりすれば、reverse()をした事も含めて全て無駄になる。

と言った、ほんとに些細な事に注意です。


■ 番号が抜ける処理後のリバースは注意
何かの処理で途中の番号が抜けた配列が出来上がる場合もありますので、 その後reverse()などを行う場合に注意が必要です。


重要:連想配列・オブジェクトのキーは数字にしない
最初の方に書いた様に数値キーのみだとindexとして扱われてしまう。
従って商品番号をキーなどにしてしまう単純配列を作る事は基本的に駄目。
var test = new Array()
test['0']= 4
test['13333']= 9
test['2']= 5
test['200']= 3
test['4400']= 2
これにsort()をかけると
bTest[0] = 2
bTest[1] = 3
bTest[2] = 4
bTest[3] = 5
bTest[4] = 9
こうなって、残り13333までundefinedになる。
元々連想配列を作ったつもりで、間違ってsort()した後に
for(key in bTest)
をしてしまうと、IE、Geckoは 5個の処理で終わるが、Operaは13333まで処理をする。
for(i=0; i<bTest.length; i++)
なんてしてしまうと、すべてのブラウザで13333まで処理が繰り返されます。
当然、キーと値のペアも失われますし悪い事だらけです。
1個でも数字以外の文字が入るキーがあればきちんと連想配列になります。 でも、別の理由でこれも危険性があります。

と言う事で、キーには数値単体を使わない連想配列を作る。または
連想配列のつもりでキーが数値だけの配列を作る場合は、 どうせ連想配列にはArrayのメソッドもlengthも使えないので、 コンストラクタをArrayではなくObjectにして
var test = new Object()
test['0']= 4
test['13333']= 9
test['2']= 5
test['200']= 3
test['4400']= 2
と言う形で。または 連想配列と同等のオブジェクトにするべき。
コンストラクタArrayを利用した連想配列には基本的にメリットは無い。
これでIEやFireFoxなどは、普通配列とオブジェクトの区別がはっきりします。

と書いたのですが・・・・
さらにOperaはコンストラクタがオブジェクトであっても、 数値単体キーが混じってると、何もしなくても数値キーは自動的に先頭に並んでしまう癖を持っています。

配列の自動整列の癖がそのままオブジェクトにまで作用しているようです。
従って連想配列を作る時は、Arrayの連想配列ではなく、必ずコンストラクタをObjectにしたオブジェクトにする。尚且つオブジェクトのキー(プロパティ)は数字(数字文字のみ)を使わない。 と言う事が安全な連想配列作りと言う事になります。
ま、これは処理中にオブジェクトに並び順を求める必要がある場合ですけどね。 連想配列なので並び順なんてどうでも良いケースも多いですから。
例:
var test = new Object()
test['ccc']= 4
test['5']= 9
test['aaa']= 5
test['1']= 3
test['ddd']= 2

これはオブジェクトなので順次処理は for(...in Object)を使います。
普通のブラウザはこの設定順に並んでいます。
Operaは 1,5,ccc,aaa,ddd と言う順に並びます。

■ 連想配列はリテラルでは書けない。
ary = []; とするとコンストラクタはArrayです。
ary = {}; とするとコンストラクタはObjectです。

ary = [9,1,4,6,1]; //普通の配列
ary = [1:2,5:3,6:4]; // キーが数値でもこういう書き方はできない

ary = {1:2,5:3,6:4}; //これは連想配列(Array)ではなくオブジェクト

ary = [ {1:2}, {3:5}, {6:8} ]
これは aryは可変indexを持つ普通の配列で、個々の要素がオブジェクト。 1次元目が普通の配列で、2次元目がオブジェクト。

このページでの『 連想配列 』という言葉の定義について。
このページでは連想配列を コンストラクタArray()で作成されている配列の内でキーが可変indexではない物を基本的に指しています。 コンストラクタがオブジェクトの場合はオブジェクトと書いています。
連想配列と言うものがキーと値のペアであるとするなら、オブジェクトも連想配列です。
が、一応コンストラクタが違う事と、キーが数値のみで構成される場合にキーに対する扱いが違ってくるので、 Arrayコンストラクタで作られている物を連想配列。Objectコンストラクタから作られてる物をオブジェクトと呼んでいます。

前述の様に『JavaScript』でのコンストラクタArrayの『配列』には特徴があるので、 連想配列 = オブジェクト として完全にイコールで結んでしまうのは微妙に違います。
連想配列(コンストラクタがArray)は、配列の特徴を持ったままです。
ary = {1:2,5:4} これはオブジェクトであってArray(配列)ではないです。
広域な言葉の定義の話は別として、JavaScirpt上のArrayObjectとObjectObjectは微妙に違う事は頭に入れて置いて下さい。

例:
ary1 = new Array();
ary1['a'] = 5;
ary1['b'] = 8;
ary1['c'] = 9;
ary1['d'] = 2;
ary1['e'] = 4;


ojb2 ={"a":5,"b":8,"c":9,"d":2,"e":4}

■ 連想配列、オブジェクトのキーの付け方
JavaScriptでは、キーが英数文字の場合は["キー"]の括弧を省いて『 . 』でつなげて指定できます。
これはwindow.document.forms. ・・・・ と同じような書き方が可能と言う事です。
が、規則があって 『数字のみ』『数字から始まる物』『ハイフンなどの記号が付く物』はこの省略ができません。 汎用性を高める為にも、英数文字だけでキーを付けましょう。
Object["bu"]["ka"]["kakari"]
Object.bu.ka.kakari

Object[0]["ka"]["kakari"]
Object[0].ka.kakari

Object["0bu"]["ka"]["kakari"]
Object["0bu"].ka.kakari

Object["eigyo-bu"]["ka"]["kakari"]
Object["eigyo-bu"].ka.kakari

記号の例は CSSのケースが特徴的

CSSで font-size を JavaScriptで指定する場合は
elementobject.style.fontSize です。

CSSで backgroundColorは JavaScriptで指定する場合は
elementobject.style.backgroundColor です。

記号だけの為ではないと思いますが、CSSの様に記号が使えないので 括弧で囲まずに済むように記号を外して後を大文字にしている物が用意されています。

■ 配列、連想配列、オブジェクトの見分け方
通常見分ける必要などないが、連想配列のコンストラクタは、
if(変数.constructor == Array)
これで『コンストラクタがArrayなら』という条件になります。
Arrayの部分をObjectにすれば、コンストラクタがオブジェクトかどうかわかります。 この時 Array、Objectには ' 'を付けない。

が、Arrayであってもキーが可変indexタイプの普通配列なのか、 キーが文字列の連想配列なのかはわかりません。 連想配列にif(ary.sort)
として確認しても、 連想配列はsort()が使えませんが、この式はtrueで通ってしまいます。

JSON(JSONP)データを受け取った場合は、constructorがArrayであれば必ず普通配列です。
コンストラクタがArrayの連想配列はJSON形式のリテラルで書けないので、JSONのデータ自体は初期段階まではほぼ確実と言えます。

他のウィンドウのJavaScriptからリテラルではなく、オブジェクトの状態で変数を受け取った場合は、 そのオブジェクトにif(変数.constructor == Array)
が使えません。
constructorをalert()すると、きちんと function Array(){ [native code ] }と出ますが、 if(変数.constructor == Array)の特殊な式は成り立ちません。
これは自windowのコアArrayとイコールか?と言う事になるようで、他ウィンドウで作った配列を 別ウィンドウで受け取った場合は式はfalseになるようです(でも情報はもっている)。 従ってこの場合はSafariの事まで考えると、
if(変数.sort)としてsortプロパティを持ってるか調べる事で、コンストラクタがArrayかどうかわかります。 が、もし連想配列やオブジェクトで sort というキーが設定されてしまってるとこれも役に立たなくなります。

Safariは alert()で見る constructor で返ってくる値は『 function 』と言う文字だけですが、 if(変数.constructor == Array)
はちゃんと成り立つようです。


■まとめ
・連想配列はArrayでは作らず、Objectでオブジェクトとして作る。

・連想配列、オブジェクトのキーには文字列型であっても数字文字単独のキーを使わない。(a1などは構わない)

・連想配列・オブジェクトにはsort,reverse,sliceなどのArrayのメソッドで使われている語は使わない。

・固定要素は、文字列型、数値型、真偽型のみにしてnullやundefinedはなるべく使わない事。(''等の空にする) (説明はしていませんが、nullやundefinedがあるとブラウザによって扱われ方が違います)

これらを守っておくと、ブラウザの違いによる結果の違いなどがおきにくくなります。


専用ページから申し込むと
So-netより高い3万円CB

案ずるより産むが易し
使ってみれば疑問も解決

XREA+ (plus) 206円/月
( お試し7日間 )

CORE SERVER 428円/月
( お試し15日間 )

ロリポップ 270円/月
( お試し期間10日間 )

ヘテムル 1620円/月
( お試し期間15日間 )

さくら 129円/月
( お試し期間2週間 )

無駄な高額ドメイン管理料金払ってませんか?

バリュードメイン
ムームードメイン

2010年で第二世代携帯がサービス終了。ソフトバンクに乗り換えて安くなった人が7割以上います!