JavaScript プログラミング講座

 

Iterator について

 


■ Iterator について

 
ECMAScript 6 世代の機能です。
 
■イテレーターとは?
 
イテレーターは、反復者(繰り返し)を意味します。
 
リストなどから、データを順番に取得するための、共通の仕様です。
 
Iterator を利用すると、データの列挙方法を、統一できます。
 

■イテレーターを供給できるデータ構造について

 
配列、連想配列、単方向リスト、双方向リスト、リングリスト など
 

■イテレーターに対応している組み込み API について

 
以下の型は、Iterable なオブジェクトです。
 
StringArrayTypedArrayMapSetGenerator など
 


 

Iterator を実装する

 
 


■ Iterable なオブジェクトについて

 
■ Iterable なオブジェクトとは?
 
イテレーターの仕様に対応し、値情報を列挙可能なオブジェクトです。
 
■ Iterable となる条件について
 
オブジェクトに対して、[Symbol.iterator]() メソッドを実装します。
 
戻り値から、新しい Iterator オブジェクトを返します。
 
Object[Symbol.iterator]( ) :Iterator
引数 Voidなし
戻り値 Iterator新しい Iterator オブジェクトが得られる。
 
■実装例
 
以下のオブジェクトは、Iterable です。
 
Iterable なオブジェクトを作成する

// ------------------------------------------------------------
// オブジェクトを作成する
// ------------------------------------------------------------
var my_obj = new Object();

// ------------------------------------------------------------
// [Symbol.iterator]() メソッドを実装する
// ------------------------------------------------------------
my_obj[Symbol.iterator] = function(){
	var iterator = new Object();
	iterator[Symbol.iterator] = function(){
		return iterator;
	};
	iterator.next = function(){
		return { done:true , value:undefined };
	};
	return iterator;
};

// ------------------------------------------------------------
// 新しい Iterator オブジェクトを取得する
// ------------------------------------------------------------
var iterator0 = my_obj[Symbol.iterator]();
var iterator1 = my_obj[Symbol.iterator]();
 
Iterable なコンストラクタを実装する

// ------------------------------------------------------------
// MyListObj コンストラクタ
// ------------------------------------------------------------
function MyListObj(){

}

// ------------------------------------------------------------
// [Symbol.iterator]() メソッドを実装する
// ------------------------------------------------------------
MyListObj.prototype[Symbol.iterator] = function (){
	var iterator = new Object();
	iterator[Symbol.iterator] = function(){
		return iterator;
	};
	iterator.next = function(){
		return { done:true , value:undefined };
	};
	return iterator;
};


// ------------------------------------------------------------
// MyListObj オブジェクトを作成する
// ------------------------------------------------------------
var my_obj = new MyListObj();

// ------------------------------------------------------------
// 新しい Iterator オブジェクトを取得する
// ------------------------------------------------------------
var iterator0 = my_obj[Symbol.iterator]();
var iterator1 = my_obj[Symbol.iterator]();
 

■ Iterator オブジェクトについて

 
■ Iterator オブジェクトとなる条件について
 
オブジェクトに対して、以下のメソッドを実装します。
 
[Symbol.iterator]() メソッドを実装します。
 
next() メソッドを実装します。
 
■ [Symbol.iterator]() メソッドについて
 
自身の Iterator オブジェクトを返します。
 
これにより、Iterator 自身も、Iterable となります。
 
■ next() メソッドについて
 
next() メソッドは、何度も呼び出されます。
 
戻り値から、新しい IteratorResult オブジェクトを返します。
 
Iterator.next( ) :IteratorResult
第01引数(略可)*(ここでは省略)
戻り値 IteratorResult新しい IteratorResult オブジェクトが得られる。
 

■ IteratorResult オブジェクトについて

 
■必要なプロパティについて
 
オブジェクトを作成し、以下のプロパティを追加します。
 
プロパティ 解説
done Boolean 列挙が最後尾まで完了したなら true
value * 今回の値情報を取得する
 

■ Iterator の実装例

 
■リングリストの場合
 
リングリストに Iterator を実装する

// ------------------------------------------------------------
// RingListItem コンストラクタ
// ------------------------------------------------------------
function RingListItem(data){
	this._prev = this;
	this._next = this;
	this.setData(data);
}
(function(){
	var self = RingListItem.prototype;

	// ------------------------------------------------------------
	// 自身の直前にアイテムを登録
	// ------------------------------------------------------------
	self.attachPrev = function(item){
		item.remove();
		var _next = this;
		var _prev = this._prev;
		_prev._next = item;
		_next._prev = item;
		item._prev = _prev;
		item._next = _next;
	};

	// ------------------------------------------------------------
	// 自身の直後にアイテムを登録
	// ------------------------------------------------------------
	self.attachNext = function(item){
		item.remove();
		var _prev = this;
		var _next = this._next;
		_prev._next = item;
		_next._prev = item;
		item._prev = _prev;
		item._next = _next;
	};

	// ------------------------------------------------------------
	// 自身を除外
	// ------------------------------------------------------------
	self.remove = function(){
		var _prev = this._prev;
		var _next = this._next;
		_prev._next = _next;
		_next._prev = _prev;
		this._prev = this;
		this._next = this;
	};

	// ------------------------------------------------------------
	// 直前のアイテムを取得
	// ------------------------------------------------------------
	self.getPrev = function(){
		return this._prev;
	};

	// ------------------------------------------------------------
	// 直後のアイテムを取得
	// ------------------------------------------------------------
	self.getNext = function(){
		return this._next;
	};

	// ------------------------------------------------------------
	// データを取得
	// ------------------------------------------------------------
	self.getData = function(){
		return this.data;
	};

	// ------------------------------------------------------------
	// データをセット
	// ------------------------------------------------------------
	self.setData = function(data){
		this.data = data;
	};

	// ------------------------------------------------------------
	// Iterator オブジェクトを作成する
	// ------------------------------------------------------------
	self[Symbol.iterator] = function(){
		var item = this;
		var last = this;
		var done = false;
		var index = 0;
		var iterator = new Object();
		iterator[Symbol.iterator] = function(){
			return iterator;
		};
		iterator.next = function(){
			done = (function(){
				if(done) return true;
				if(index == 0) return false;
				var next = item.getNext();
				if(next === last) return true;
				if(next === item) return true;
				item = next;
				return false;
			})();
			var result = new Object();
			result.done = done;
			if(done){
				result.value = undefined;
			}else{
				result.value = item.getData();
				index += 1;
			}
			return result;
		};
		return iterator;
	};

})();


// ------------------------------------------------------------
// RingListItem オブジェクトを作成する
// ------------------------------------------------------------
var ring_item0 = new RingListItem( "aaa" );
var ring_item1 = new RingListItem( "bbb" );
var ring_item2 = new RingListItem( "ccc" );
var ring_item3 = new RingListItem( "ddd" );
var ring_item4 = new RingListItem( "eee" );

// ------------------------------------------------------------
// リングリストを接続する
// ------------------------------------------------------------
ring_item0.attachNext( ring_item1 );
ring_item1.attachNext( ring_item2 );
ring_item2.attachNext( ring_item3 );
ring_item3.attachNext( ring_item4 );


// ------------------------------------------------------------
// for..of 文を使用して順番に列挙する
// ------------------------------------------------------------
var value;
for (value of ring_item0){
	console.log( value );
}

// ------------------------------------------------------------
// Iterable なオブジェクトから配列を生成する
// ------------------------------------------------------------
var ary = Array.from(ring_item0);

// 出力テスト
console.log(ary); // [ "aaa" , "bbb" , "ccc" , "ddd" , "eee" ]


// ------------------------------------------------------------
// Iterator オブジェクトによる列挙を確認する
// ------------------------------------------------------------
// Iterator オブジェクトを生成する
var iterator = ring_item0[Symbol.iterator]();

// 値情報を順番に列挙する
var result = iterator.next();
while(!(result.done)){
	console.log( result.value );
	result = iterator.next();
}
 
■双方向リストの場合
 
双方向リストに Iterator を実装する

// ------------------------------------------------------------
// LinkedList コンストラクタ
// ------------------------------------------------------------
function LinkedList(){
	this._list = new LinkedListItem();
}
(function(){
	var self = LinkedList.prototype;

	// ------------------------------------------------------------
	// 最先頭にアイテムを登録
	// ------------------------------------------------------------
	self.attachFirst = function(item){
		item.remove();
		var _next = this._list;
		var _prev = _next._prev;
		_prev._next = item;
		_next._prev = item;
		item._prev = _prev;
		item._next = _next;
		item._null = false;
	};

	// ------------------------------------------------------------
	// 最後尾にアイテムを登録
	// ------------------------------------------------------------
	self.attachLast = function(item){
		item.remove();
		var _prev = this._list;
		var _next = _prev._next;
		_prev._next = item;
		_next._prev = item;
		item._prev = _prev;
		item._next = _next;
		item._null = false;
	};

	// ------------------------------------------------------------
	// Iterator オブジェクトを作成する
	// ------------------------------------------------------------
	self[Symbol.iterator] = function(){
		var item = this._list;
		var done = false;
		var iterator = new Object();
		iterator[Symbol.iterator] = function(){
			return iterator;
		};
		iterator.next = function(){
			done = (function(){
				if(done) return true;
				var next = item.getNext();
				if(!next) return true;
				item = next;
				return false;
			})();
			var result = new Object();
			result.done = done;
			if(done){
				result.value = undefined;
			}else{
				result.value = item.getData();
			}
			return result;
		};
		return iterator;
	};

})();

// ------------------------------------------------------------
// LinkedListItem コンストラクタ
// ------------------------------------------------------------
function LinkedListItem(data){
	this._prev = this;
	this._next = this;
	this._null = true;
	this.setData(data);
}
(function(){
	var self = LinkedListItem.prototype;

	// ------------------------------------------------------------
	// 自身の直前にアイテムを登録
	// ------------------------------------------------------------
	self.attachPrev = function(item){
		if(this._null) return false;
		item.remove();
		var _next = this;
		var _prev = this._prev;
		_prev._next = item;
		_next._prev = item;
		item._prev = _prev;
		item._next = _next;
		item._null = false;
		return true;
	};

	// ------------------------------------------------------------
	// 自身の直後にアイテムを登録
	// ------------------------------------------------------------
	self.attachNext = function(item){
		if(this._null) return false;
		item.remove();
		var _prev = this;
		var _next = this._next;
		_prev._next = item;
		_next._prev = item;
		item._prev = _prev;
		item._next = _next;
		item._null = false;
		return true;
	};

	// ------------------------------------------------------------
	// 自身を除外
	// ------------------------------------------------------------
	self.remove = function(){
		var _prev = this._prev;
		var _next = this._next;
		_prev._next = _next;
		_next._prev = _prev;
		this._prev = this;
		this._next = this;
		this._null = true;
	};

	// ------------------------------------------------------------
	// 直前のアイテムを取得
	// ------------------------------------------------------------
	self.getPrev = function(){
		var item = this._prev;
		if(item._null) return null;
		return item;
	};

	// ------------------------------------------------------------
	// 直後のアイテムを取得
	// ------------------------------------------------------------
	self.getNext = function(){
		var item = this._next;
		if(item._null) return null;
		return item;
	};

	// ------------------------------------------------------------
	// データを取得
	// ------------------------------------------------------------
	self.getData = function(){
		return this.data;
	};

	// ------------------------------------------------------------
	// データをセット
	// ------------------------------------------------------------
	self.setData = function(data){
		this.data = data;
	};

})();



// ------------------------------------------------------------
// LinkedList オブジェクトを作成する
// ------------------------------------------------------------
var linked_list = new LinkedList();

// ------------------------------------------------------------
// LinkedListItem オブジェクトを作成する
// ------------------------------------------------------------
var item0 = new LinkedListItem( "aaa" );
var item1 = new LinkedListItem( "bbb" );
var item2 = new LinkedListItem( "ccc" );
var item3 = new LinkedListItem( "ddd" );
var item4 = new LinkedListItem( "eee" );

// ------------------------------------------------------------
// リストを接続する
// ------------------------------------------------------------
linked_list.attachLast( item0 );
item0.attachNext( item1 );
item1.attachNext( item2 );
item2.attachNext( item3 );
item3.attachNext( item4 );


// ------------------------------------------------------------
// for..of 文を使用して順番に列挙する
// ------------------------------------------------------------
var value;
for (value of linked_list){
	console.log( value );
}

// ------------------------------------------------------------
// Iterable なオブジェクトから配列を生成する
// ------------------------------------------------------------
var ary = Array.from(linked_list);

// 出力テスト
console.log(ary); // [ "aaa" , "bbb" , "ccc" , "ddd" , "eee" ]


// ------------------------------------------------------------
// Iterator オブジェクトによる列挙を確認する
// ------------------------------------------------------------
// Iterator オブジェクトを生成する
var iterator = linked_list[Symbol.iterator]();

// 値情報を順番に列挙する
var result = iterator.next();
while(!(result.done)){
	console.log( result.value );
	result = iterator.next();
}
 


 

Iterator オブジェクトを取得する

 


■ Iterator オブジェクトを取得する

 
■ Iterator オブジェクトを取得する
 
[Symbol.iterator]() メソッドを使用します。
 
Iterable なオブジェクトであれば、必ず実装されています。
 
Object[Symbol.iterator]( ) :Iterator
引数 Voidなし
戻り値 Iterator新しい Iterator オブジェクトが得られる。
 

■その他の取得用メソッドについて

 
別途、取得用メソッドが用意されている場合もあります。
 
組み込み機能の一例です。
 
■ Array クラス
 
メソッド 説明
entries() Iterator オブジェクトを取得する(番地と値の列挙)
keys() Iterator オブジェクトを取得する(番地の列挙)
values() Iterator オブジェクトを取得する(値の列挙)*
 
■ Map クラス
 
メソッド 説明
entries() Iterator オブジェクトを取得する(キーと値の列挙) *
keys() Iterator オブジェクトを取得する(キーの列挙)
values() Iterator オブジェクトを取得する(値の列挙)
 
■ Set クラス
 
メソッド 説明
entries() Iterator オブジェクトを取得する(キーと値の列挙)
values() Iterator オブジェクトを取得する(値の列挙) *
 

■近似メソッドについて(自身が Iterable ではない場合)

 
Iterator オブジェクトではなく、Array オブジェクトが得られます。
 
Array オブジェクトは、Iterable です。
 
■ Object クラス
 
メソッド 説明
Object.entries() Array オブジェクトを取得する(キーと値の列挙)
Object.keys() Array オブジェクトを取得する(キーの列挙)
Object.values() Array オブジェクトを取得する(値の列挙)
 


 

Iterator を使ってデータを列挙する

 
 


■値情報を順番に列挙する(Iterator オブジェクト)

 
■ Iterator オブジェクトを取得する
 
こちらで解説しています。
 
■ Iterator オブジェクトから、値情報を列挙する
 
next() メソッドを使用します。
 
next() メソッドを、何度も呼び出す事で、値情報を順番に取り出せます。
 
Iterator.next( ) :IteratorResult
第01引数(略可)*(ここでは省略)
戻り値 IteratorResult新しい IteratorResult オブジェクトが得られる。
 
■IteratorResult オブジェクトについて
 
プロパティ 解説
done Boolean 列挙が最後尾まで完了したなら true
value * 今回の値情報を取得する
 
■使用例
 
Iterator オブジェクトと next() メソッドの動作を確認する

// ------------------------------------------------------------
// 配列を作成する
// ------------------------------------------------------------
var ary = ["aaa" , "bbb" , "ccc" , "ddd" , "eee"];

// ------------------------------------------------------------
// Iterator オブジェクトを取得(値の列挙)
// ------------------------------------------------------------
var iterator = ary[Symbol.iterator]();

// ------------------------------------------------------------
// 出力テスト
// ------------------------------------------------------------
console.log( iterator.next() ); // { done:false , value:"aaa" }
console.log( iterator.next() ); // { done:false , value:"bbb" }
console.log( iterator.next() ); // { done:false , value:"ccc" }
console.log( iterator.next() ); // { done:false , value:"ddd" }
console.log( iterator.next() ); // { done:false , value:"eee" }
console.log( iterator.next() ); // { done:true  , value:undefined }
console.log( iterator.next() ); // { done:true  , value:undefined }
console.log( iterator.next() ); // { done:true  , value:undefined }
 
while 文を使って、値情報を順番に取得する

// ------------------------------------------------------------
// 配列を作成する
// ------------------------------------------------------------
var ary = ["aaa" , "bbb" , "ccc" , "ddd" , "eee"];

// ------------------------------------------------------------
// Iterator オブジェクトを取得(値の列挙)
// ------------------------------------------------------------
var iterator = ary[Symbol.iterator]();

// ------------------------------------------------------------
// 値情報を順番に列挙する
// ------------------------------------------------------------
var result = iterator.next();
while(!(result.done)){
	console.log( result.value );
	result = iterator.next();
}
 

■値情報を順番に列挙する(for..of 文)

 
for..of 文を使用すると、値情報を順番に列挙できます。
 
of 文の次に、Iterable なオブジェクトを指定します。
 
■使用例
 
for..of 文を使って、値情報を順番に取得する(Array オブジェクトの場合)

// ------------------------------------------------------------
// 配列を作成する
// ------------------------------------------------------------
var ary = ["aaa" , "bbb" , "ccc" , "ddd" , "eee"];

// ------------------------------------------------------------
// 値情報を順番に列挙する(Iterable なオブジェクトを指定)
// ------------------------------------------------------------
var value;
for(value of ary){
	console.log( value );
}
 
for..of 文を使って、値情報を順番に取得する(Map オブジェクトの場合)

// ------------------------------------------------------------
// Map オブジェクトを作成する
// ------------------------------------------------------------
var map = new Map();

// ------------------------------------------------------------
// 適当なデータを格納する
// ------------------------------------------------------------
map.set( "aaa" , "あ" );
map.set( "bbb" , "い" );
map.set( "ccc" , "う" );
map.set( "ddd" , "え" );
map.set( "eee" , "お" );

// ------------------------------------------------------------
// 値情報を順番に列挙する
// ------------------------------------------------------------
var key,value;
for([key,value] of map){
	console.log( key , value );
}
 
■ Iterator オブジェクトを指定した場合
 
Iterator は、Iterable の条件を満たしています。
 
Iterator の、[Symbol.iterator]() メソッドは、自身の参照を返します。
 
新しい Iterator を生成するわけではありません。
 
よって、for..of 文に Iterator を渡すと、中身が更新されます。
 
for..of 文に Iterator を指定した場合の動作を確認する

// ------------------------------------------------------------
// 配列を作成する
// ------------------------------------------------------------
var ary = ["aaa" , "bbb" , "ccc" , "ddd" , "eee"];

// ------------------------------------------------------------
// Iterator オブジェクトを取得
// ------------------------------------------------------------
var iterator = ary[Symbol.iterator]();

// ------------------------------------------------------------
// 値情報を順番に列挙する(Iterator を渡すと中身が更新される)
// ------------------------------------------------------------
var value;
for(value of iterator){
	console.log( value );
}
console.log( iterator.next() ); // { done:true  , value:undefined }
 

■すべての値情報をまとめて取得する(Array オブジェクトを取得)

 
Array.from() メソッドを使用します。
 
Array.from( iterable ) :Array
第01引数 *Iterable なオブジェクトを指定
戻り値 Array新しい Array オブジェクトが得られる
 
■使用例
 
文字列から、Array オブジェクトを生成する

// ------------------------------------------------------------
// 適当な文字列を用意
// ------------------------------------------------------------
var str = "あいうえお";

// ------------------------------------------------------------
// 文字列から、Array オブジェクトを生成する
// ------------------------------------------------------------
var ary = Array.from(str);

// 出力テスト
console.log(ary); // [ "あ" , "い" , "う" , "え" , "お" ]
 

■すべての値情報をまとめて取得する(Map オブジェクトを取得)

 
Map() コンストラクタを使用します。
 
new Map( iterable ) :Map
第01引数 *Iterable なオブジェクトを指定。値情報は、[ "キー" , 値 ]
戻り値 Map新しい Map オブジェクトが得られる
 
■使用例
 
ジェネレーターから、Map オブジェクトを生成する

// ------------------------------------------------------------
// ジェネレーター関数(オブジェクトから [ "キー" , 値 ] を列挙)
// ------------------------------------------------------------
function* Object_Entries(obj){
	var key;
	for(key in obj){
		var value = obj[key];
		yield [ key , value ];
	}
};

// ------------------------------------------------------------
// 適当なオブジェクトを用意
// ------------------------------------------------------------
var dictionary = {
	"aaa":"あ",
	"bbb":"い",
	"ccc":"う",
	"ddd":"え",
	"eee":"お"
};

// ------------------------------------------------------------
// ジェネレーターを生成
// ------------------------------------------------------------
var generator = Object_Entries(dictionary);

// ------------------------------------------------------------
// ジェネレーターから、Map オブジェクトを生成する
// ------------------------------------------------------------
var map = new Map(generator);

// 出力テスト
console.log(map); // { "aaa":"あ" , "bbb":"い" , "ccc":"う" , "ddd":"え" , "eee":"お" }