Posted at

【js】x.map is not a functionの解決方法とそれから理解したこと

More than 1 year has passed since last update.


概要

javascriptでmapメソッドを使いたかったが以下のエラーが出てきたのでメモ。

配列っぽいデータを加工したい場合はmapメソッドを使っとけばいい、と思っていたが、それは曖昧な理解ですべての場合で使えるわけではなかったので、問題の解決とともにしっかりと掘り下げてみた。

Uncaught TypeError: x.map is not a function


今回やろうとしたこと

inputタグを利用してファイルを複数選択できるようにし、そのファイル群をmapメソッドで処理するというもの。

ただ、今回のケースと違っていても同じ考え方で解決できる問題はあるはず。


ファイルを複数選択する単純なもの

<html>

<body>
<input type="file" multiple onchange="selectFile()" id="file"/>
</body>
</html>


わかったこと


なぜmapが使えなかったか。

そもそも私が何気なく利用していたmapはArray.prototype.map()というものであった。

Array.prototypeってなに、となるがこれはプロトタイプチェーンや型の構造について理解すれば解決する。この記事が理解の助けになった。

mapメソッドを利用するためには操作対象のオブジェクトがmapメソッドを持つ、もしくは__proto__プロパティがmapメソッドを持つ必要がある。Array型のオブジェクトは__proto__プロパティにmapメソッドを持つためmapをすることができる。このことが前述の


私が何気なく利用していたmapはArray.prototype.map()というものであった。


という部分を説明してくれている。

今回私が操作したいデータをみてみると以下のデータであり、Arrayではないし、FileListの__proto__プロパティにmapメソッドが定義されていないためmapが使えないのも納得である。しかしFileListは頑張れば繰り返し処理をさせられそうな形である。であれば、どうすればmapを利用可能にできるかが知りたい。

FileList {0: File(147802), 1: File(370312), length: 2}

とりあえず調べてみるとstackoverflowにそれらしいことが書いてあったので一部を引用する。


FileList is not an Array, and does not inherit from Array, but it does implement the iterable protocol, so you can use the spread syntax to get it as an array.


FileListはiterable protocolを実装しているらしい。そしてspread syntaxを使えばArrayとしてデータを取ってこられるらしい。私にはiterable protocolspread syntaxがわからなかったが、この2つの単語を理解できれば解決もすぐそこだと思うので、もう少し調べることにした。


iterable protocolとはなんなのか。

MDN - 反復処理プロトコルの抜粋だが下記のように述べられている。


iterable (反復可能)プロトコルによって、JavaScriptオブジェクトは、for..of構造で値がループしているもの等の反復動作を定義、または、カスタマイズできます。ArrayやMapのように、いくつかのビルトインの型はデフォルトの反復動作を持っているビルトイン iterables です。一方、(Objectのような)他の型は反復動作を持ちません。


簡単に言うとiterable protocolを実装していることは反復可能、であるということがわかる。同記事の反復可能オブジェクトを期待する構文に述べられているが、以下の方法でiterable protocolを実装したオブジェクトの値を取得することができる。

// 例1

for(let value of ["a", "b", "c"]){
console.log(value);
}
// "a"
// "b"
// "c"

// 例2
[..."abc"]; // ["a", "b", "c"]

この例2を見てみると[...変数]の形式を利用すると配列を取得できることがわかる。

これによりIterable protocolを実装しているがArrayオブジェクトではないFileListオブジェクトはArrayオブジェクトに無事に変換できることがわかる。


spread syntaxとはなんなのか

MDN - スプレッド構文を見ると明らかになったが、先の「iterable protocolとはなんなのか。」で登場した例2のコードがまさにこれである。3点リーダーで変数を展開することをspread syntaxという、これだけのことである。

これで以下の文言をしっかりと理解することができた。


FileList is not an Array, and does not inherit from Array, but it does implement the iterable protocol, so you can use the spread syntax to get it as an array.


そして私はこれを駆使して無事にFileList型からmapメソッドを利用できるようになった。


その他


Iterableか確認する

これも先ほどから抜粋しているstackoverflowで述べられているが操作対象のオブジェクトがIterableであるかは

変数名[Symbol.iterator]()

で確認することができる。


参考

https://stackoverflow.com/questions/44511925/how-to-map-through-an-array-of-input-files

https://qiita.com/howdy39/items/35729490b024ca295d6c

http://lightbulbcat.hatenablog.com/entry/2018/02/04/060811

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Iteration_protocols#Syntaxes_expecting_iterables

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax