【TypeScript】enumをforEach等で列挙する
更新日:2022/11/21
TypeScriptのenum型のキーや値を、forEach()で列挙してみます。
なお今回のコードはenum型オブジェクトの生成が必要なので、const enumは対象外です。
値をforEachする
enum型に対して forEach()で値を列挙する場合、まずはObject.entries()でenum型のプロパティを配列化します。
次に、その配列に forEach() を適用します。
使用例
次の enum型を対象にしてみます。
enum DayWeek1{ SAN, MON, TUE, WEN=30, THU, FRI, SAT,}
このenum型を Object.entries() で配列化して、forEach() を適用します。
Object.entries(DayWeek1).forEach(([key,value])=>{
console.log( `${key}:\t${value}` );
})
解説
Object.entries()は [ キー , 値 ] を1要素とした配列を生成します。
forEach()は、配列の要素を一つずつコールバック関数に渡します。
コールバック関数の引数が [key,value] となっていますが、これはJavaScriptの分割代入という機能です。
次のように書き直すと、イメージがつかみやすいかもしれません。
forEach((e)=>{ let [key,value] = e; }
逆引き用キーが付加されている
コードの実行結果は次のようになります。
0: SAN 1: MON 2: TUE 30: WEN 31: THU 32: FRI 33: SAT SAN: 0 MON: 1 TUE: 2 WEN: 30 THU: 31 FRI: 32 SAT: 33
キーに0や1などの数値が含まれています。
これは、次のように逆引きのために生成されています。
const propName = DayWeek1[30]; // propName : "WEN"
値に文字列をセットした場合は、逆引き用のキーはセットされません。
enum DayWeek2{ SAN="日曜日",MON="月曜日"}
Object.entries(DayWeek2).forEach(([key,value])=>{
console.log( `${key}:\t${value}` );
})
結果は、enumで定義したものだけです。
SAN: 日曜日 MON: 月曜日
数値インデックスを除去
数値インデックスが必要ない時は、除外します。
JavaScriptのプロパティ名は全て文字列です。
数値インデックスも文字列としてセットされています。
そのため、文字列が数値文字列かどうかをチェックします。
次のコードは、数値文字列ではない時に true を返します。
const isNotNumberString = (key:string)=>isNaN(Number(key));
これを使用して、前項のコードを数値インデックスを除外するように変更します。
Object.entries(DayWeek1).forEach(([key,value])=>{
if( !isNaN(Number(key))) return; // 数値インデックスの場合終了
console.log( `${key}:\t${value}` );
})
上のコードはforEach()内で数値インデックスのチェックを行っています。
forEach()の前に、filter()メソッドで不要な要素を除去しておく方法もあります。
Object.entries(DayWeek1)
.filter(([key,])=>isNaN(Number(key)))
.forEach(([key,value])=>{
console.log( `${key}:\t${value}` );
})
forEach()内で数値判定した方が効率はいいです。
しかし、filter()メソッドで配列の内容にフィルターをかけていることを明示できるので、コードの意図をつかみやすいかもしれません。
どちらも、実行結果は次のようになります。
SAN: 0 MON: 1 TUE: 2 WEN: 30 THU: 31 FRI: 32 SAT: 33
数値インデックスが除去されていますね。
for...inを使ったenum列挙
for...in構文を使用すると、enum型のキーを列挙できます。
enum DayWeek1{ SAN, MON, TUE, WEN=30, THU, FRI, SAT,}
for( const key in DayWeek1){
console.log( `${key}:\t${DayWeek1[key]}` )
}
for...in構文もforEach()のケースと同じように、数値インデックスを列挙します。
数値インデックスを除去したいときは、キーをチェックします。
for( const key in DayWeek1){
if( !isNotNumberString(key) ) continue;
console.log( `${key}:\t${DayWeek1[key]}` )
}
for...in構文は配列で使用しない方がいいとか、プロトタイプチェーン上の列挙可能なプロパティも対象なので意図しない結果になりやすいとかで、注意が必要です。
しかし、enum型をそのまま使うならfor...in構文でも問題ないです。たぶん。
ちなみに、Object.keys()の結果にforEach()を適用すると、for...inと同様の結果を得られます。
Object.keys(DayWeek1).forEach((key)=>{
if( !isNotNumberString(key)) return; // 数値インデックスの場合終了
console.log( `${key}:\t${DayWeek1[key]}` );
})
for...ofを使ったenum列挙
配列等のイテラブルなオブジェクトにfor...of構文を使用すると、値を列挙できます。
const array = ["a","b","c"];
for( const value of array){
console.log( value ); // 結果 : a
// b
// c
}
しかし、enum型はイテラブルなオブジェクトではないので、for...of構文を使用できません。
そこで考えられるのが、Object.values()でenum型の値を配列に変換します。
const DayWeek1Array = Object.values(DayWeek1);
for( const value of DayWeek1Array){
console.log( value );
}
数値インデックスを除去したいときは、 Object.entries().filter()の後に、map()で値を抜き出します。
const DayWeek1Array = Object.entries(DayWeek1)
.filter(e=>isNotNumberString(e[0]))
.map(e=>e[1]);
for( const value of DayWeek1Array){
console.log( value );
}
しかし処理の流れから見ると、無理やりfor...ofを使っている印象を受けます。
forEach()を呼び出した方が自然な気がしますね。
そこで、enum型をイテラブルにしてみます。
enum DayWeek1{ SAN, MON, TUE, WEN=30, THU, FRI, SAT,}
DayWeek1[Symbol.iterator] = function* (){
yield* Object.values( DayWeek1 );
}
for( const value of DayWeek1){ // エラー:Type 'typeof DayWeek1' must have a '[Symbol.iterator]()' method that returns an iterator.
console.log( value );
}
※エラー内容: タイプ 'typeof DayWeek1' には、反復子を返す '[Symbol.iterator]()' メソッドが必要です。
トランスパイルでエラーが出力されました…
コードは生成されているので実行はできますが、それではダメなんでしょうね…
更新日:2022/11/21
関連記事
スポンサーリンク
記事の内容について
こんにちはけーちゃんです。
説明するのって難しいですね。
「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。
裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。
掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。
ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php
このサイトは、リンクフリーです。大歓迎です。