【JavaScript】オブジェクトがイテラブルなオブジェクトかどうか判断する方法

更新日:2023/04/24

JavaScriptには値を反復機能を組み込んだイテラブルなオブジェクトがあります。
今回はオブジェクトがイテラブルかどうかを判断して、値を列挙したり配列に変換する方法をお伝えします。

 

イテラブルなオブジェクトの確認

引数で与えられた値がイテラブルなオブジェクトかどうかは、次のような関数で確認できます。

イテラブルなオブジェクトの確認関数

const isIterable = obj => 
    typeof obj !== "object" || obj === null 
        ? false 
        : typeof obj[Symbol.iterator] === "function";

イテラブルなオブジェクトは[Symbol.iterator]というプロパティを持っていて、ここに値を反復する関数(イテレーター)がセットされています。
そこで、[Symbol.iterator]プロパティの型が "function" かどうかを typeof演算子でチェックしています。

イテレーターについては、次のページを読んでみてください。
【JavaScript】 Iterator(イテレーター)とは?避けて通りたいけど説明してみる

一通りテストしてみます。

console.log( isIterable( 123 ) ); // false
console.log( isIterable( "abc" ) ); // false
console.log( isIterable( 123n ) ); // false
console.log( isIterable( true ) ); // false
console.log( isIterable( Symbol() ) ); // false
console.log( isIterable( null ) ); // false
console.log( isIterable( undefined ) ); // false
console.log( isIterable( function(){} ) ); // false
console.log( isIterable( [1,2,3] ) ); // true
console.log( isIterable( {a:1,b:2} ) ); // false
console.log( isIterable( new Set( [1,2,3] ) )); // true
console.log( isIterable( new Uint8Array( [1,2,3] ) ));// true

想定した結果になりました。

イテラブルだと確認したら、for...of等で値を列挙できます。

イテラブルか確認して列挙

if( !isIterable( v) )  console.log( "列挙できません" );
else for( const value of v ) console.log( value );
最初に作成したコードは次のようなものでした。

const isIterable = obj => 
     typeof obj?.[Symbol.iterator] === "function";

簡潔でいいですね。ほぼ問題なく機能します。
しかしこのコードは文字列を渡すと、イテラブルなオブジェクトと判断します。

文字列からプロパティにアクセスするとStringオブジェクトに変換されますが、このオブジェクトがイテラブルなオブジェクトだからです。
仕方がないので、引数がオブジェクトかどうかのチェックを入れました。

 

イテラブルなオブジェクトを配列に変換する

オブジェクトがイテラブルのとき値を配列で返し、イテラブルでないときはそのまま返す関数を作成します。

イテラブルなオブジェクトを配列に変換

const toArrayFromIterable = obj =>
    !Array.isArray(obj) && isIterable(obj) 
        ? [...obj] : obj;

...はスプレッド構文です。
イテラブルなオブジェクトに使うと値を反復して返してくれます。
その結果に[]を使うと、配列になります。

最初に配列かどうかをチェックしているのは、一度展開して配列に戻すというムダな処理を避けるのが目的です。

一通りテストしてみます。

console.log( toArrayFromIterable( 123 ) );  // 123
console.log( toArrayFromIterable( "abc" ) );  // abc
console.log( toArrayFromIterable( 123n ) );  // 123n
console.log( toArrayFromIterable( true ) );  // true
console.log( toArrayFromIterable( Symbol() ) );  // Symbol()
console.log( toArrayFromIterable( null ) );  // null
console.log( toArrayFromIterable( undefined ) );  // undefined
console.log( toArrayFromIterable( function(){} ) );  // function ()
console.log( toArrayFromIterable( [1,2,3] ) );  // Array(3) [ 1, 2, 3 ]
console.log( toArrayFromIterable( {a:1,b:2} ) );  // Object { a: 1, b: 2 }
console.log( toArrayFromIterable( new Set( [1,2,3] ) ));  // Array(3) [ 1, 2, 3 ]
console.log( toArrayFromIterable( new Uint8Array( [1,2,3] ) ));  // Array(3) [ 1, 2, 3 ]

想定した結果になりました。

 

オブジェクトの値を文字列で出力する

オブジェクトの値を文字列で出力してみます。

配列はtoString()メソッドで内容を出力できます。

[1,2,3].toString() → "1,2,3"

しかし他のイテラブルオブジェクトは出力してくれません。

(new Set([1,2,3])).toString() → "[object Set]"

そこで一度配列に変換して、文字列化します。

その他の問題も考慮して次のような関数を作成しました。

オブジェクトの値を文字列化する関数

const getValueString = value =>{
    if( value === undefined ) return "undefined";
    if( value === null ) return "null";

    const type = typeof value;
    switch(type){
        case "string": return "\"" + value + "\"";
        case "bigint": return value.toString() + "n";
        case "object":
             return `${ getObjectName(value) } ${
                 isIterable( value ) 
                    ? "[" +  [...value].toString() + "]"
                    :  JSON.stringify(value)
                }`;
    }

    return value.toString();
}

getObjectName()は他ページで紹介している関数で、オブジェクトを生成したコンストラクター関数の名前を取得しています。

オブジェクトの名前を取得するコード

const getObjectName = obj =>{
  if( typeof obj !=="object" ) return typeof obj;
  const proto = Object.getPrototypeOf(obj);

  return typeof proto?.constructor !== "function" || proto.constructor.name === undefined 
          ? "object" : proto.constructor.name;
};

【JavaScript】組み込みやAPIオブジェクトの名前を取得する方法より

使用すると、次のような結果になります。

console.log( getValueString( 123 ) );  // 123
console.log( getValueString( "abc" ) );  // "abc"
console.log( getValueString( 123n ) );  // 123n
console.log( getValueString( true ) );  // true
console.log( getValueString( Symbol() ) );  // Symbol()
console.log( getValueString( null ) );  // null
console.log( getValueString( undefined ) );  // undefined
console.log( getValueString( function(){} ) );  // function(){}
console.log( getValueString( [1,2,3] ) );  // Array [1,2,3]
console.log( getValueString( {a:1,b:2} ) );  // Object {"a":1,"b":2}
console.log( getValueString( new Set( [1,2,3] ) ));  // Set [1,2,3]
console.log( getValueString( new Uint8Array( [1,2,3] ) ));  // Uint8Array [1,2,3]

更新日:2023/04/24

書いた人(管理人):けーちゃん

スポンサーリンク

記事の内容について

null

こんにちはけーちゃんです。
説明するのって難しいですね。

「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。

裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。

掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。

ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php

 

このサイトは、リンクフリーです。大歓迎です。