ArrayとObjectの判別とkey:valueの取得方法

このQ&Aのポイント
  • 質問文章では、Array型とObject型を判別する方法と、Object型の場合にkey:valueを連結する方法が必要です。
  • Array型とObject型の判別はtypeof演算子を使用して行います。typeof演算子の結果が'object'であれば、Array型またはObject型と判断できます。
  • Object型の場合にkey:valueを連結するには、for...inループを使用してオブジェクトのキーと値を取得し、連結します。連結時には、config.connect.operatorを使用してキーと値を連結します。
回答を見る
  • ベストアンサー

引数の文字列を全て連結する関数について

var config={ connect:{character:',',expand:true,operator:':'} /* character……何の文字列で連結するか expand……展開するかどうか(true/false or number) 数字の場合は、展開する回数 operator……オブジェクト( {a:'a',b:'b'}など )の場合、キーと値を連結する文字列(key+operator+value) */ }; function connect(){ var connectString=config.connect.character,expand=config.connect.expand,operator=config.connect.operator,n=0; var subCallee=function(arg){ var text=arg,i; if(typeof arg=='object'){ var number=arg.length; switch(expand){ case true: text=subCallee(arg[0]); foreach(arg,function(i,key,value){ //i……ループ回数-1 key……Object:キー,Array:i value……値 if(i){ //0回目はすでに再帰呼び出し済み text+=connectString+subCallee(value); //再帰呼び出しで、展開 } }); break; case false: case 0: //展開回数0(0)もしくは、展開しない(false)場合 text=arg[0]; foreach(arg,function(s){ if(s){ text+=connectString+this; //展開しないので、そのまま引数をつなげる。 } }); break; default: if(typeof expand=='number'){ //expandがNumber型だった時 if(n>expand){ //nは最初に定義済み。展開回数が、指定された回数を越えた場合には、終了。 break; } foreach(arg,function(i,key,value){ i(i){ text+=connectString+subCallee(value); } }); n++; //何回展開したか、数える。 break; } return false; } } return text; }; return subCallee(arguments); //最初にargumentsを渡して、subCalleeを呼ぶ。 } このように定義されていますが、Array型とObject型はtypeofしても、共にobjectが帰ってくるため、判別できません。 何か判別する方法はないでしょうか? また、Object型の場合には、valueをkey+config.connect.operator+value(つまり、key:valueのような感じ)にしたいのですが、 text+=character+key+operator+subCallee(value); とし、 var a=config.connect; a.character='<>'; alert(connect({a:0,b:1})); としましたが、alert表示されたのは、 undefined<>b:1 でした; Object,Arrayの判定はしてないので、Arrayを入れてみたら↓ alert(connect(['a','b',['c',['d']]])); ↓ a<>1:b<>2:c<>1:d となっています。 展開順に考えて、以下のようになるのが妥当かな、と思ったのですが; まず、['a','b',[]] なので、 0:a<>1:b<>[] で、次に[]の中の、['c',[]]が展開されるので、 <>2:c<>[] 最後に、['d']が展開されるので、 <>1:d よって、 0:a<>1:b<>2:c<>1:d かな、と。。 実際はa<>b<>c<>dでいいので、ここは気にしなくていいですw Object,Arrayの判定方法と、Objectの場合、key:valueを取得する方法を、宜しくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
  • think49
  • ベストアンサー率59% (285/482)
回答No.7

今更ですが…、JSON を利用するのではダメなんでしょうか? https://developer.mozilla.org/En/Using_native_JSON function connect () { return JSON.stringify(Array.prototype.slice.call(arguments)); } connect(1, 2, 3, {foo: 'hoge1', piyo: 'hoge2'}); // [1,2,3,{"foo":"hoge1","piyo":"hoge2"}] CSV の派生フォーマットを利用しているようですが、文字列とオブジェクトの境界があいまいな気がします。 connect("0:a", {0:a}); // 0:a<>0:a JSON なら両者を区別できますし、パースするのも簡単です。

gorusura
質問者

補足

ほとんどのブラウザで動作するのでしょうか? 主要ブラウザはしっかりとクロスブラウザ対策をしようと思っています。 IE,Firefox,NN,Opera,Chrome,Safariです。 色々と申し訳ありません。。 実は、他の質問に回答中のときに、間違えてsub.txtに保存してしまって、詳しい内容を消してしまいました。 sub.txtを更新しましたので、見てくださいm(_ _)m http://kyukyoku.xrsp.net/cgi-bin/sample/sub.txt ただ、毎回expandをswitchで回しているので、無駄な感じがします…… 良い方法はないでしょうか?

その他の回答 (6)

  • Chaire
  • ベストアンサー率60% (79/130)
回答No.6

次のような関数があるとき、 function forEach (o, ...) { if (o instanceof Array) return ...; if (o instanceof RegExp) return ...; if (o instanceof Object) return ...; } // 最低 1 回、最大 3 回の instanceof 判定が常につきまとう forEach (o, ...); 次のように表現し直すことができます。 Array.prototype.forEach = function () { return ...; }; RegExp.prototype.forEach = function () { return ...; }; Object.prototype.forEach = function () { return ...; }; // o の「型」に応じた forEach が実行され、何の判定もいらない o.forEach (...); ゆえに、そもそも論として『Object、Array の判定方法』なるものが必要なのか、という所から考え直してはいかがですか。 ここで問題があるとすれば、俗に「オブジェクト汚染」と呼ばれる組み込みオブジェクトの拡張の是非ですが、適当にやって下さい。Array.prototype.forEach()、Object.keys() などは ECMAscript 5 に含まれましたので標準として扱って良いでしょう。周りが行儀の良いスクリプトだけであるなら、forEach() の有無が Array を示す目印にもなります。 ※実際には forEach() より reduce()、あるいは map() を使った方が早そうですけどね。 一瞬だけ Array.prototype.toString()、Object.prototype.toString() を乗っ取り、後で戻すのも一つの手ではあります。危険性は RegExp.$1 なんかと大差ないでしょうから。

gorusura
質問者

補足

申し訳ないです。 Object.prototypeはIEだと無理なので、オブジェクト汚染は決してやらないように、心掛けていますm(_ _)m 以前はバンバン汚染していましたが、オブジェクト汚染というのを発見して、全ての関数を汚染しないように書き換えたばかりでした;;

  • think49
  • ベストアンサー率59% (285/482)
回答No.5

#2-4 です。 オブジェクトのプロパティ展開は以下のようにして実現できます。 http://jsbin.com/asago4/2/ # 面倒なので再帰処理はやってませんが、同じ仕組みで可能だと思います。

  • think49
  • ベストアンサー率59% (285/482)
回答No.4

#2-3 です。 > Object.prototype.toString.callがまずい、ということで、 Object 型以外のデータに Object.prototype.toString.call(arg) するのがまずいのであって Object 型に使用するのは問題ないです。 さておき、http://kyukyoku.xrsp.net/cgi-bin/download/sub.txt のやり方には少し問題があります。 function kindof (arg) {  alert(arg instanceof Object); // true  alert(arg instanceof Array); // true  if (arg instanceof Array) {   return "array";  } else if (arg instanceof Object) {   return "object";  } } alert(kindof([])); // "array" 期待通り、"array" を返しますが、instanceof のテスト順が逆なら "object" を返します。 同じ理屈で(arg instanceof Array && arg instanceof RegExp) === true な arg が渡されたとき "array" を返しますが、"regexp" を返してもおかしくないですね。 また、"object" が返された時、「arg instanceof Object」なのか「native かつ [[Call]] を持たない Object 型」なのかを区別しづらいです。("function" も同じ) false を返すケースも「host かつ [[Call]] を持たない Object 型」です。 ひとつのオブジェクトに対して [[Class]] はひとつしか存在しませんが、プロトタイプはプロトタイプチェーンでたどれる数だけ存在します。 Object.prototype.toString なら if-else 方式の検査で済みますが、isntanceof では問題があります。 instanceof を活用できるのは arg が Array.prototype.slice を持っているか検査する場合です。 Array を継承しているオブジェクトなら Array.prototype.slice も持っていると期待できます。(※slice をオーバーライドしている場合を除く) [[Class]] は継承しませんので、Array を継承したオブジェクトを "Array" とみなしません。 質問者さんはどちらの動作を期待しているでしょうか? if (getClass(arg) === 'Array') { // Object 型を判別して、[[Class]] を得る  alert('arg is a [object Array]'); } if (isObject(arg) && Object.prototype.toString.call(arg) === '[object Array]') { // 同上  alert('arg is a [object Array]'); } if (isObject(arg) && arg instanceof Array) { // Object 型を判別して、プロトタイプを検査する  alert('arg is a [object Array]'); } https://gist.github.com/887049 https://gist.github.com/862098

  • think49
  • ベストアンサー率59% (285/482)
回答No.3

#2 です。 > 前回の質問で私が書いたコードも同じようなことをしています。(今見ると Object 型の判定が甘かったですね…。) すみません。よく見たら「型」のチェックを全くやっていませんでした…。 型チェック付の isArray を下記URLにUPしておきました。 http://jsfiddle.net/qCF3B/3/ > foreach(arg,function(s){ 細かいことですが、foreach は JavaScript にはないですね。 Array.forEach のような関数をユーザ定義しているのでしょうか。 掲示されたコードは動きませんでした。

  • think49
  • ベストアンサー率59% (285/482)
回答No.2

まず、型を判定したいのか、[[Class]] を判定したいのか、をはっきりさせてください。 ES5 に Array 型は存在しませんし、typeof arg === "object" だと Null 型で true を返してしまう上に一部の Object 型を取り逃がします。 https://gist.github.com/862085 https://gist.github.com/887049 配列は Object 型で [[Class]] === "Array" でインスタンス生成元(コンストラクタ) が Array です。 console.log(typeof []); // "object" console.log(Object.prototype.toString.call([])); // "[object Array]" console.log([] instanceof Array); // true ES5 の Array.isArray(arg) では次のように判定しています。 http://es5.github.com/#x15.4.3.2 1. arg の型が Object 型でなければ false を返す 2. [[Class]] === "Array" ならば true を返す 3. false を返す [[Class]] は Object 型だけに存在する内部プロパティなのでまず、型を判定してから [[Class]] を判定しています。 前回の質問で私が書いたコードも同じようなことをしています。(今見ると Object 型の判定が甘かったですね…。) http://okwave.jp/qa/q6590566.html Object.prototype.toString は [[Class]] を返す便利なメソッドですが、ひとつ注意点があります。 ES3 規定によれば、初めに [[Class]] プロパティを得ることになっていますが、そもそも Object 型でなければ [[Class]] がありませんので、String 型や Null 型を渡してどういう結果になるか予想できません。(最悪、TypeError を出力して処理がとまるかもしれません。) http://www2u.biglobe.ne.jp/~oz-07ams/prog/ecma262r3/15-2_Object_Objects.html#section-15.2.4.2 ES5 ではこの規定は更新され、Undefined 型、Null 型はそれぞれ [object Undefined], [object Null] を返し、"[object " + class + "]" を返す仕様になっています。 http://es5.github.com/#x15.2.4.2 対象ブラウザを最新のブラウザに限定するなら別ですが、古いブラウザにも対応するなら ES5 で規定された動作は期待できません。 IE6 では Object.prototype.toString.call(null) === "[object Object]" です。ES5 の規定に従っていないことがわかります。(IE8 も同じだったと思います) ES5 規定の Array.isArray と同じように「型」をチェックしてから [[Class]] をチェックする手法が無難だと思います。

gorusura
質問者

お礼

ご丁寧に、ありがとうございました^^

gorusura
質問者

補足

Object.prototype.toString.callがまずい、ということで、 http://yas-hummingbird.blogspot.com/2009/07/javascript.html こちらを参考にして、getType関数を作ってみました。 判定は出来ましたが、指定された回数、展開をする、というのと、オブジェクトの場合には、キー+separator+値 で取得、というのがどうしてもできませんでした>< 関数群に関しては、以下のファイルにまとめておきました。 具体的な質問内容に関しても、そちらに記載しています。 http://kyukyoku.xrsp.net/cgi-bin/download/sub.txt 要望が多くて申し訳ないですが、御回答宜しくお願いします。

  • fujillin
  • ベストアンサー率61% (1594/2576)
回答No.1

よくわかってませんが… >Object,Arrayの判定方法と、Objectの場合、key:valueを取得する方法を~ 判定方法には以下が利用できませんでしょうか?  Object.prototype.toString.call(array) // [object Array]  Object.prototype.toString.call(object) // [object Object] Objectのkeyとvalueは、こんなのでは? var obj = {   c : 1,  b : 2,  a : 3,  hoge : function(){ alert("OK"); } }; //obj = window; var key, value, result = []; for(key in obj){  value = obj.key?obj.key:obj[key];  result.push(key + " / " + value); } alert(result.join("\n"));

gorusura
質問者

お礼

ありがとうございます。 参考になりました。 Object.prototype.toStringは、初めて聞きました^^

関連するQ&A

  • 特定の文字列で連結して返す関数

    String.prototype.connect=function(){ var connectString=this,text=''; foreach(arguments,function(){ //for(var i in arguments){}とfor(i=0;i<i.length;i++){}を使い分けされています if(text){ text+=connectString; } text+=this; }); return text; }; このように、thisに指定された文字列で、引数にある文字列を連結する関数を作ったのですが、 例: ' - '.connect('a','b','c'); ->a - b - c 配列などを渡した場合も表示したいのですが、typeof this=='object'で確認し、foreach(this,function(){/*~*/}); だと、その配列の中に更に配列がある場合、もう一度チェックし、foreachをやらなければなりません。 これではキリがないので、どうしたらいいでしょうか? arguments.calleeや、whileなどでどうにかしようと思いましたが、難しくて出来ませんでした>< やりたいことは、こうです↓ 'or'.connect('あ',['い',['う','え','お'],'か',['き'],['く','け'],'こ','さ'],'し','す','せ',[[['そ']][['た','ち','つ','て','と']]]); ->あorいorうorえorおorかorきorくorけorこorさorしorすorせorそorたorちorつorてorと 複雑かもしれませんが、お力をお貸しくださいm(_ _)m

  • jscriptで空白を含む文字列の引数が途切れる

    abc.batから user01.js "A01 B01" "X01" として実行したとき ----------------------------------- user01.jsでは、 var objArg = WScript.Arguments; var arg01,arg02; arg01= objArg.item(0); arg02= objArg.item(1);  が実行され arg01は、"A01" arg02は、"B01" となります 期待しているのは arg01は、"A01 B01" arg02は、"X01"       です デリミッタが空白なので何か問題があるのでしょうが・・・わかりません お知恵をお願いします

  • ある文字を含む文字列のみ配列にする方法

    var_dump($text); を行うと array(1) { [0]=> string(XX) "あいさつ" } array(1) { [0]=> string(XX) "いい日旅立ち" } array(1) { [0]=> string(XX) "りんご、うまい" } array(1) { [0]=> string(XX) "メロン好き" } …… となるような変数 $text があり、そこから $key = array("ばなな","りんご","メロン"); の配列内にある文字列を含むものだけを新たに格納したいです。 自分では foreach($text as $value){ if(in_array($value, $key)){ $key_text[] = $value; } } と書いて試したのですが、NULLと返ってきてしまいます。 どうやって書けばいいか教えて下さい。 よろしくお願いします。

    • ベストアンサー
    • PHP
  • jQuery.eachは第2引数以上は作れないのでしょうか?

    jQuery.eachは第2引数以上は作れないのでしょうか? 配列として3つ設定したい場合はできないものなのでしょうか? var list = { "#load1": "A.html .ld1":"aaa1", "#load2": "B.html .ld2":"bbb2", "#load3": "C.html .ld3":"ccc3" }; jQuery.each(list, function(key,val, arg) { $(key).load(val, function(Text, status) { if ($(key).text() == "A") $(arg).replaceWith("<img src='baby.png'/>"); }); }); こんな書き方は出来なかったので・・・ 第3引数があるか調べてみたものの第2までしか書いていないのでないと思っています。 しかし3つ以上の場合はどのようにしたらいいのでしょうか?

  • 引数のない関数

    何度もすいませんが教えてください。 function MM_preloadImages() { //v3.0 var d=document; if(d.images){ if(!d.MM_p) d.MM_p=new Array(); var i,j=d.MM_p.length,a=MM_preloadImages.arguments; for(i=0; i<a.length; i++) if (a[i].indexOf("#")!=0){ d.MM_p[j]=new Image; d.MM_p[j++].src=a[i];}} } 上記の関数ですが、 <body onLoad="MM_preloadImages('img/aaa.gif','img/bbb.gif','img/ccc.gif')> という使い方をしています。 引数が宣言されていなくても成り立つのはなぜでしょうか。 教えてください。

  • 関数名をテキストから読み込む方法(文字列比較無し)

    C言語にて開発を行っています。 今回、テキストファイルに記述された関数名および引数を読み取り、プログラム側でこれを実行する処理を実装しようと考えています。 その際、テキストから読み込んだ文字列によって処理を振り分けることを省略したいと思っていますが方法が分からず困っています。 以下が読み取る対象のテキストファイルの例です。 @function_A;arg1;arg2 @function_B;arg1;arg2 ・・・ このとき、テキストファイルから読み取った文字列に応じた関数を実行する、というところまでは実装できています。 (読み取った文字列がfunction_Aであれば、同名の関数を実行します) 現在の実装は、以下のようなイメージです。 command = "読み取った文字列(@function_Aなど)"; if(command == "@function_A"){ function_A(arg1, arg2);} else if(command == "@function_B"){ function_B(arg1, arg2);} ・・・ しかし、この実装では関数が増えるに従って if(command == "function_X"){...}をどんどん追記していく必要があります。 今後関数の数が膨大になっていく予定なので、これを追記せずに読み込んだ文字列と同名の関数を実行できるようにしたいと思っています。 以下の様なイメージです。 function_A.cというファイルに関数function_A(arg1, arg2)を実装します。 同様にして、 function_B.cに関数function_B(arg1, arg2)を実装します。 main()内にて、 char command[] = "読み取った文字列(function_Aなど)"; command(arg1, arg2); とすることによって、function_A()関数を実行できるようにすることが目的です。 もちろん、この場合のcommandはchar型ですから、上記のような記法が不可能なことは分かっていますが、似たようなことを実装したいと思っています。 どのようにすれば実装できるのでしょうか。 今後、関数の数だけfunction_A.cのようなファイルが増え、最終的に200近くになる予定ですし、 if(command == "@function_A"){ function_A(arg1, arg2);} else if(command == "@function_B"){ function_B(arg1, arg2);} ・・・ の記述忘れによる動作不良を防ぐためにもこれを実装したいと思います。

  • 引数が変わってしまいます。

    AC3で開発を行っております。 複数のオブジェクト(変数の種類は同じ)を配列で確保し、 一気に処理をしようとしているのですが、引数の動作がうまくいきません。 以下が問題のソースです。 var peat:Array ; var fg1:Object = new Object(); var fg2:Object = new Object(); fg1 = { _sp:Sprite, _img:Loader , _url:String , _x:int, _y:int }; fg2 = { _sp:Sprite, _img:Loader , _url:String , _x:int, _y:int }; peat = new Array( fg1 ); peat.push( fg2 ); for(i = 0;i < 2;i++) { // 値の代入 // クリック時の処理 peat[i].sp.addEventListener( MouseEvent.CLICK , function(event:MouseEvent):void{ test(event, arguments.callee, peat[i]); } ); } クリックしたときに それぞれのオブジェクトを参照したいのですが、 どちらも peat[2] を参照しているようなのです。 (for文の後に i=0 とすると先に設定したほうは正しく動作します。) AC3での開発が初めてなので、変数の宣言から怪しいかも知れませんが ご助力お願いいたします。

    • ベストアンサー
    • Flash
  • 関数化のやり方

    function bold(){ if(document.all){ //IE var str=document.selection.createRange().text; if (str != "") { document.selection.createRange().text="<b>" + str + "</b>"; } else { document.form1.CBBE_TEXT.value += "<b></b>" } } else { //Firefox var el=document.getElementById('text'); var sPos = el.selectionStart; var ePos = el.selectionEnd; var str = el.value.substring(sPos, ePos); el.value = el.value.substring(0, sPos) + "<b>" + str + "</b>" + el.value.substr(ePos); } } function italic(){ if(document.all){ //IE var str=document.selection.createRange().text; if (str != "") { document.selection.createRange().text="<i>" + str + "</i>"; } else { document.form1.CBBE_TEXT.value += "<i></i>" } } else { //Firefox var el=document.getElementById('text'); var sPos = el.selectionStart; var ePos = el.selectionEnd; var str = el.value.substring(sPos, ePos); el.value = el.value.substring(0, sPos) + "<i>" + str + "</i>" + el.value.substr(ePos); } } を <input type="button" value="太字" onclick="bold()"> <input type="button" value="斜字" onclick="italic()"> で制御しています。 関数化できませんか?

  • イベントに独自引数を渡したい。

    ACTIONSCRIPT3.0で、外部ファイルを読み込ませる処理を行っているのですが、読み込み完了時の関数に独自の引数を渡すことは不可能なのでしょうか? var j:int = 100; var myArray:Array; for(var i=0;i<j;i++){  myArray[i] = new Loader();  myArray[i].load(new URLRequest(別の配列に入っている文字列のURL))  myArray[i].contentLoaderInfo.addEventListener(Event.INIT,myfunc); } function myfunc(e){イベント発生時の処理} のように、多数のイベント処理を書くときに配列のインデックスによって処理を分けたいのですが、上の例だとイベント発生時の関数であるmyfuncの引数は規定オブジェクトなので独自のものが渡せません。 いまは別にグローバルな配列を作成して処理をしているのですが、メモリ常駐になることや、オブジェクト指向として気に入らない状態です。 イベントが発生したオブジェクトの名前をキーにすることも考えましたが、どの道スマートではない。なにか方法はないものでしょうか? また、言語仕様的にやむをえないのでしょうか?

    • ベストアンサー
    • Flash
  • javascriptの標準仕様で用意されているオブジェクトを拡張しても

    javascriptの標準仕様で用意されているオブジェクトを拡張してもよいのかしら?  例えば、Arrayオブジェクトを拡張して降順ソートをやりたい時、   data=[1,2,3,4];   data.sort(function(a,b){return(b-a);});   alert(data);  のように使うのは、仕様にあるので許されると思います。   Array.prototype.descending=function(){    this.sort(function(a,b){return(b-a);});    return this;   }   data=[1,2,3,4];   alert(data.descending());  のようにArrayオブジェクトを拡張して使って問題ないでしょうか?  あるいは、   function myArray(arg){    this.descending=function(){     arg.sort(function(a,b){return(b-a);});     return arg;    }   }   data=[1,2,3,4];   data=new myArray(data);   alert(data.descending()); のように使うべきものなのでしょうか?

専門家に質問してみよう