Javascriptを使うなら理解しておきたいアルゴリズム - 抽出・ソート・結合・集計

Javascriptでのアルゴリズムの書き方の覚書です。

説明に使用するデータ構造

アルゴリズムの説明のために、以下のようなオブジェクトの配列を使います。

var countries = [];

countries.push( { name: "日本",             currency: "JPY", population: 127156000 } );
countries.push( { name: "フランス",         currency: "EUR", population: 65073482 } );
countries.push( { name: "スペイン",         currency: "EUR", population: 44904000 } );
countries.push( { name: "ロシア",           currency: "RUB", population: 141903979 } );
countries.push( { name: "ベトナム",         currency: "VND", population: 84238000 } );
countries.push( { name: "カンボジア",       currency: "KHR", population: 14805000 } );
countries.push( { name: "コートジボワール", currency: "XOF", population: 44904000 } );

このオブジェクトの配列は、以下のようなコードでブラウザに表示できます。

document.write( "<table border='1'>" );

// テーブルヘッダを出力する。
document.write( "<tr>" );
document.write( "<th>国名</th>" );
document.write( "<th>通貨</th>" );
document.write( "<th>人口</th>" );
document.write( "</tr>" );

// 配列中のオブジェクト毎に以下を繰り返す。
for ( var i = 0; i < countries.length; ++i ) {
	var country = countries[i];
	
	// テーブルデータを出力する。
	document.write( "<tr>" );
	document.write( "<td>" + country["name"] + "</td>" );
	document.write( "<td>" + country["currency"] + "</td>" );
	document.write( "<td>" + country["population"] + "</td>" );
	document.write( "</tr>" );
}

document.write( "</table>" );

このコードの実行結果は以下の通りです。

国名通貨人口
日本JPY127156000
フランスEUR65073482
スペインEUR44904000
ロシアRUB141903979
ベトナムVND84238000
カンボジアKHR14805000
コートジボワールXOF44904000

■抽出

複数のデータの中から、特定の条件を満たすデータを抽出するアルゴリズムです。

例として、商品データproductsから特定の条件を満たすデータを抽出します。

var products = [];

products.push( { product_id: "1010", product_name: "えんぴつ",   price: 80 } );
products.push( { product_id: "1020", product_name: "ボールペン", price: 100 } );
products.push( { product_id: "1030", product_name: "消しゴム",   price: 100 } );
products.push( { product_id: "2010", product_name: "定規",       price: 140 } );
products.push( { product_id: "2020", product_name: "コンパス",   price: 300 } );
products.push( { product_id: "3010", product_name: "のり",       price: 200 } );

商品データproductsをブラウザに表示すると、以下のようになります。

商品ID商品名価格
1010えんぴつ80
1020ボールペン100
1030消しゴム100
2010定規140
2020コンパス300
3010のり200


以下のコードは、商品データproductsから「price > 100」を満たすデータを抽出します。

// 抽出結果を格納する配列
var results = [];

// すべての商品データについて繰り返す。
for ( var i = 0; i < products.length; ++i ) {
	var product = products[i];
	
	// 商品データのpriceが100より大きいことを確認する。
	if ( product.price > 100 ) {
		// 商品データを抽出結果に追加する。
		results.push( product );
	}
}

抽出結果は、変数resultsに格納されます。抽出結果resultをブラウザに表示すると、以下のようになります。

商品ID商品名価格
2010定規140
2020コンパス300
3010のり200

■ソート

複数のデータを、特定の条件でソートするアルゴリズムです。

Javascriptでの配列のソート

ソートの例の前に、Javascriptでの配列のソートについて説明します。
配列をソートするには、sort()メソッドを使用します。

var array = [13, 43, 22, 234];

array.sort( function( a, b ) {
	// 引数1と引数2はそれぞれ配列中の値。

	// 引き算により引数の大小を比較する。
	// ・引数2の方が大きければ、戻り値は正の値: 引数2→引数1の順に並ぶ。
	// ・引数同士が一致すれば、戻り値は0: 引数1と引数2は同列。
	// ・引数1の方が大きければ、戻り値は負の値: 引数1→引数2の順に並ぶ。
	return b - a;
} );

ソートの並び順は、sort()メソッドの引数に渡す比較関数で決めます。
比較関数には引数として配列の要素が2つずつ渡されます。引数2→引数1の順に並べたい場合は正の値を、引数1と引数2を同列にしたい場合は0を、引数1→引数2の順に並べたい場合は負の値を、比較関数の戻り値として返します。
なお、上記のコードは配列の要素を「数値の降順」にソートします。

ソートの例(五十音順)

それでは、ソートの例を挙げます。
例として、売上データsalesを特定の項目の「五十音順」にソートします。

var sales = [];

sales.push( { sales_date: "20090702", sales_no: "00001", product_id: "1020", amount: 10 } );
sales.push( { sales_date: "20090702", sales_no: "00002", product_id: "1010", amount: 5 } );
sales.push( { sales_date: "20090702", sales_no: "00003", product_id: "3010", amount: 20 } );
sales.push( { sales_date: "20090703", sales_no: "00001", product_id: "1030", amount: 10 } );
sales.push( { sales_date: "20090703", sales_no: "00002", product_id: "1020", amount: 15 } );
sales.push( { sales_date: "20090704", sales_no: "00001", product_id: "2020", amount: 30 } );
sales.push( { sales_date: "20090704", sales_no: "00002", product_id: "3010", amount: 20 } );

売上データsalesをブラウザに表示すると、以下のようになります。

売上日売上番号商品ID数量
2009070200001102010
200907020000210105
2009070200003301020
2009070300001103010
2009070300002102015
2009070400001202030
2009070400002301020


以下のコードは、売上データsalesを「五十音順でsales_noの昇順」にソートします。。

sales.sort( function ( sale1, sale2 ) {
	// 引数1と引数2はそれぞれ売上データ。
	
	// 売上データからsales_noを取得する。
	var sales_no1 = sale1["sales_no"];
	var sales_no2 = sale2["sales_no"];
	
	////////////////////////////////////////////
	// sales_noを比較する。
	////////////////////////////////////////////
	
	if ( sales_no1 === sales_no2 ) {
		// sales_noが一致する場合: 引数1と引数2は同列。
		return 0;
	}
	
	if ( sales_no1 > sales_no2 ) {
		// 引数1のsales_noの方が大きい: 引数2→引数1の順に並べる
		return 1;
	} else {
		// 引数1のsales_noの方が小さい: 引数1→引数2の順に並べる
		return -1;
	}
});

ソート後の売上データsalesをブラウザに表示すると、以下のようになります。

売上日売上番号商品ID数量
2009070200001102010
2009070300001103010
2009070400001202030
200907020000210105
2009070300002102015
2009070400002301020
2009070200003301020

ソートの例(数値順)

例をもう一つ挙げます。商品データproductsを特定の項目の「数値順」にソートします。

var products = [];

products.push( { product_id: "2010", product_name: "定規",       price: 140 } );
products.push( { product_id: "2020", product_name: "コンパス",   price: 300 } );
products.push( { product_id: "3010", product_name: "のり",       price: 200 } );
products.push( { product_id: "1030", product_name: "消しゴム",   price: 100 } );
products.push( { product_id: "1020", product_name: "ボールペン", price: 100 } );
products.push( { product_id: "1010", product_name: "えんぴつ",   price: 80 } );

商品データproductsをブラウザに表示すると、以下のようになります。

商品ID商品名価格
2010定規140
2020コンパス300
3010のり200
1030消しゴム100
1020ボールペン100
1010えんぴつ80


以下のコードは、商品データproductsを「数値順でproduct_idの降順」にソートします。

products.sort( function ( product1, product2 ) {
	// 引数1と引数2はそれぞれ商品データ。

	// 商品データのproduct_idを取得する。
	var product_id1 = product1["product_id"];
	var product_id2 = product2["product_id"];
	
	////////////////////////////////////////////
	// product_idを比較する。
	////////////////////////////////////////////

	// 引き算によりproduct_idの大小を比較する。
	// ・引数2のproduct_idの方が大きければ、戻り値は正の値: 引数2→引数1の順に並ぶ。
	// ・product_idが一致すれば、戻り値は0: 引数1と引数2の同列。
	// ・引数1のproduct_idの方が大きければ、戻り値は負の値: 引数1→引数2の順に並ぶ。
	return product_id2 - product_id1;
});

ソート後の商品データproductsをブラウザに表示すると、以下のようになります。

商品ID商品名価格
3010のり200
2020コンパス300
2010定規140
1030消しゴム100
1020ボールペン100
1010えんぴつ80

■結合

2種類のデータを、特定の条件で結合するアルゴリズムです。

例として、商品データproductsと売上データsalesを結合します。

var products = ;

products.push( { product_id: "1010", product_name: "えんぴつ",   price: 80 } );
products.push( { product_id: "1020", product_name: "ボールペン", price: 100 } );
products.push( { product_id: "1030", product_name: "消しゴム",   price: 100 } );
products.push( { product_id: "2010", product_name: "定規",       price: 140 } );
products.push( { product_id: "2020", product_name: "コンパス",   price: 300 } );
products.push( { product_id: "3010", product_name: "のり",       price: 200 } );

var sales = ;

sales.push( { sales_date: "20090702", sales_no: "00001", product_id: "1020", amount: 10 } );
sales.push( { sales_date: "20090702", sales_no: "00002", product_id: "1010", amount: 5 } );
sales.push( { sales_date: "20090702", sales_no: "00003", product_id: "3010", amount: 20 } );
sales.push( { sales_date: "20090703", sales_no: "00001", product_id: "1030", amount: 10 } );
sales.push( { sales_date: "20090703", sales_no: "00002", product_id: "1020", amount: 15 } );
sales.push( { sales_date: "20090704", sales_no: "00001", product_id: "2020", amount: 30 } );
sales.push( { sales_date: "20090704", sales_no: "00002", product_id: "3010", amount: 20 } );

商品データproductsをブラウザに表示すると以下のようになります。

商品ID商品名価格
1010えんぴつ80
1020ボールペン100
1030消しゴム100
2010定規140
2020コンパス300
3010のり200
また、売上データsalesをブラウザに表示すると以下のようになります。
売上日売上番号商品ID数量
2009070200001102010
200907020000210105
2009070200003301020
2009070300001103010
2009070300002102015
2009070400001202030
2009070400002301020


以下のコードは、商品データと売上データをproduct_idをキーとして結合します。

/////////////////////////////////////////////////////////////////////////////////
// ハッシュテーブルを作成し、 product_idをキーとして商品データを取得できるようにする。
/////////////////////////////////////////////////////////////////////////////////

// ハッシュテーブル
var hash_table = {};

// すべての商品データについて繰り返す。
for ( var i = 0; i < products.length; ++i ) {
	var product = products[i];
	
	// 商品データのproduct_idを取得する。
	var key = product["product_id"];

	// product_idをキーとして、商品データをハッシュテーブルに格納する。
	hash_table[key] = product;
}

/////////////////////////////////////////////////////////////////////////////////
// 売上データと商品データを結合する。
/////////////////////////////////////////////////////////////////////////////////

// すべての売上データについて繰り返す。
for ( var i = 0; i < sales.length; ++i ) {
	var sale = sales[i];

	// 売上データのproduct_idを取得する。
	var product_id = sale["product_id"];

	// product_idをキーとして、商品データをハッシュテーブルから取得する。
	var product = hash_table[product_id];
	if ( product !== undefined ) {
		// 商品データを取得できた場合:
		
		// 商品データの値を売上データに格納する。
		sale["product_name"] = product["product_name"];
		sale["price"] = product["price"];
	}
}

結合後の売上データsalsesをブラウザに表示すると、以下のようになります。

売上日売上番号商品ID商品名数量価格
20090702000011020ボールペン10100
20090702000021010えんぴつ580
20090702000033010のり20200
20090703000011030消しゴム10100
20090703000021020ボールペン15100
20090704000012020コンパス30300
20090704000023010のり20200

■集計

複数のデータを特定の条件で集計するアルゴリズムです。

例として、売上データsalesを集計します。

var sales = [];

sales.push( { sales_date: "20090702", sales_no: "00001", product_name: "ボールペン", price: 100, amount: 10 } );
sales.push( { sales_date: "20090702", sales_no: "00002", product_name: "えんぴつ",   price: 80,  amount: 5 } );
sales.push( { sales_date: "20090702", sales_no: "00003", product_name: "のり",       price: 200, amount: 20 } );
sales.push( { sales_date: "20090703", sales_no: "00001", product_name: "消しゴム",   price: 100, amount: 10 } );
sales.push( { sales_date: "20090703", sales_no: "00002", product_name: "ボールペン", price: 100, amount: 15 } );
sales.push( { sales_date: "20090704", sales_no: "00001", product_name: "コンパス",   price: 300, amount: 30 } );
sales.push( { sales_date: "20090704", sales_no: "00002", product_name: "のり",       price: 200, amount: 20 } );

売上データsalesをブラウザに表示すると以下のようになります。

売上日売上番号商品名数量価格
2009070200001ボールペン10100
2009070200002えんぴつ580
2009070200003のり20200
2009070300001消しゴム10100
2009070300002ボールペン15100
2009070400001コンパス30300
2009070400002のり20200


以下のコードは、salesを集計してsales_date毎の合計数量と合計売上高を求めます。

///////////////////////////////////////////////////////
// 集計結果の初期化
///////////////////////////////////////////////////////

// 集計結果を一時的に格納するオブジェクト
var resultsObj = {};

// すべての売上データについて繰り返す。
for ( var i = 0; i < sales.length; ++i ) {
	var sale = sales[i];
	
	// 売上データのsales_dateを取得する。
	var sales_date = sale["sales_date"];
	
	if ( resultsObj[sales_date] === undefined ) {
		// オブジェクトにキーsales_dateが含まれていない:
		
		// 集計結果の初期値を生成する。
		var result = { "sales_date": sales_date, "amount": 0, "total": 0 };
	
		// 生成した初期値をキーsales_dateに関連付けてオブジェクトに格納する。
		resultsObj[sales_date] = result;
	}
}

///////////////////////////////////////////////////////
// salesを集計
///////////////////////////////////////////////////////

// すべての売上データについて繰り返す。
for ( var i = 0; i < sales.length; ++i ) {
	var sale = sales[i];
	
	// 売上データのsales_dateを取得する。
	var sales_date = sale["sales_date"];
	
	// キーsales_dateに関連付く集計結果をオブジェクトから取得する。
	var result = resultsObj[sales_date];

	// 売上データのamountを集計結果に足し合わせる。
	result["amount"] += sale["amount"];
	// 売上データのpriceとamountとを掛けた値を集計結果に足し合わせる。
	result["total"] += sale["price"] * sale["amount"];
}

///////////////////////////////////////////////////////
// 集計結果を配列に移し替え
///////////////////////////////////////////////////////

var results = [];

// 一時オブジェクトの全てのキーとデータについて繰り返す。
for ( var sales_date in resultsObj ) {
	var result = resultsObj[sales_date];
	
	// データを配列に追加する
	results.push( result );
}

集計結果は変数resultsに格納されます。集計結果resultsをブラウザに表示すると以下のようになります。

売上日合計数量合計売上高
20090702355400
20090703252500
200907045013000