Bashを使うなら理解しておきたいアルゴリズム - 抽出・ソート・結合・集計
説明に使用するデータ構造
アルゴリズムの説明のために、以下のようなパイプ区切り文字列の配列を使います。
COUNTRIES=("${COUNTRIES[@]}" "日本|JPY|127156000") COUNTRIES=("${COUNTRIES[@]}" "フランス|EUR|65073482") COUNTRIES=("${COUNTRIES[@]}" "スペイン|EUR|44904000") COUNTRIES=("${COUNTRIES[@]}" "ロシア|RUB|141903979") COUNTRIES=("${COUNTRIES[@]}" "ベトナム|VND|84238000") COUNTRIES=("${COUNTRIES[@]}" "カンボジア|KHR|14805000") COUNTRIES=("${COUNTRIES[@]}" "コートジボワール|XOF|44904000")
この文字列の配列は、以下のようなコードでHTMLテーブルとして出力できます。
# テーブルヘッダを出力する。 echo "<table border='1'>" echo "<tr>" echo "<th>国名</th>" echo "<th>通貨</th>" echo "<th>人口</th>" echo "</tr>" # 配列中の文字列毎に以下を繰り返す。 for (( I=0; I < ${#COUNTRIES[@]}; ++I )) do COUNTRY=${COUNTRIES[$I]} NAME=`echo $COUNTRY | awk -F'|' '{print $1}'` CURRENCY=`echo $COUNTRY | awk -F'|' '{print $2}'` POPULATION=`echo $COUNTRY | awk -F'|' '{print $3}'` echo "<tr>" echo "<td>$NAME</td>" echo "<td>$CURRENCY</td>" echo "<td>$POPULATION</td>" echo "</tr>" done echo "</table>"
このコードの実行結果は以下の通りです。
国名 | 通貨 | 人口 |
---|---|---|
日本 | JPY | 127156000 |
フランス | EUR | 65073482 |
スペイン | EUR | 44904000 |
ロシア | RUB | 141903979 |
ベトナム | VND | 84238000 |
カンボジア | KHR | 14805000 |
コートジボワール | XOF | 44904000 |
■抽出
複数のデータの中から、特定の条件を満たすデータを抽出するアルゴリズムです。
例として、商品データPRODUCTSから特定の条件を満たすデータを抽出します。
PRODUCTS=("${PRODUCTS[@]}" "1010|えんぴつ|80") PRODUCTS=("${PRODUCTS[@]}" "1020|ボールペン|100") PRODUCTS=("${PRODUCTS[@]}" "1030|消しゴム|100") PRODUCTS=("${PRODUCTS[@]}" "2010|定規|140") PRODUCTS=("${PRODUCTS[@]}" "2020|コンパス|300") PRODUCTS=("${PRODUCTS[@]}" "3010|のり|200")
商品データPRODUCTSをHTMLテーブルとして出力すると、以下のようになります。
商品ID | 商品名 | 価格 |
---|---|---|
1010 | えんぴつ | 80 |
1020 | ボールペン | 100 |
1030 | 消しゴム | 100 |
2010 | 定規 | 140 |
2020 | コンパス | 300 |
3010 | のり | 200 |
以下のコードは、商品データPRODUCTSから「価格 > 100」を満たすデータを抽出します。
# すべての商品データについて繰り返す。 for (( I=0; I < ${#PRODUCTS[@]}; ++I )) do PRODUCT=${PRODUCTS[$I]} PRICE=`echo $PRODUCT | awk -F'|' '{print $3}'` # 商品データの価格が100より大きいことを確認する。 if [ $PRICE -gt 100 ]; then # 商品データを抽出結果に追加する。 RESULTS=("${RESULTS[@]}" "$PRODUCT") fi done
抽出結果は、配列RESULTSに格納されます。抽出結果RESULTSをHTMLテーブルとして出力すると、以下のようになります。
商品ID | 商品名 | 価格 |
---|---|---|
2010 | 定規 | 140 |
2020 | コンパス | 300 |
3010 | のり | 200 |
■ソート
複数のデータを、特定の条件でソートするアルゴリズムです。
Bashでの配列のソート
ソートの例の前に、Bashでの配列のソートについて説明します。
配列をソートするには、一旦配列の内容をファイルに書き込み、ファイルをsortコマンドでソートします。その後、ファイルの内容を読み込み、再度配列に格納します。
#!/bin/bash ARRAY=("${ARRAY[@]}" 13) ARRAY=("${ARRAY[@]}" 43) ARRAY=("${ARRAY[@]}" 22) ARRAY=("${ARRAY[@]}" 234) # 配列の内容をファイル/tmp/sort.inに書き込み cat /dev/null > /tmp/sort.in for (( I=0; I < ${#ARRAY[@]}; ++I )) do A=${ARRAY[$I]} echo $A >> /tmp/sort.in done # ファイル/tmp/sort.inを数値の降順ソートし、ソート結果をファイル/tmp/sort.outに出力 sort -n -r /tmp/sort.in > /tmp/sort.out # ファイル/tmp/sort.outの内容を読み込み、配列に格納 while read LINE do RESULTS=("${RESULTS[@]}" $LINE) done < /tmp/sort.out
ソートの並び順は、sortコマンドのオプションで決めます。例えば、以下のオプションが役立ちます。
オプション | 説明 |
---|---|
-t区切り文字 | ファイルを列に区切るための区切り文字を指定します。 |
-kキー列の番号 | ソートのキーに使用する列の番号を指定します。 |
-d | キーの五十音順に並べ替えます。 |
-n | キーの数値順に並べ替えます。 |
-r | キーの降順に並べ替えます。 |
なお、上記のコードは配列の要素を「数値の降順」にソートします。
ソートの例(五十音順)
それでは、ソートの例を挙げます。
例として、売上データSALESを特定の項目の「五十音順」にソートします。
SALES=("${SALES[@]}" "20090702|00001|1020|10") SALES=("${SALES[@]}" "20090702|00002|1010|5") SALES=("${SALES[@]}" "20090702|00003|3010|20") SALES=("${SALES[@]}" "20090703|00001|1030|10") SALES=("${SALES[@]}" "20090703|00002|1020|15") SALES=("${SALES[@]}" "20090704|00001|2020|30") SALES=("${SALES[@]}" "20090704|00002|3010|20")
売上データSALESをHTMLテーブルとして出力すると、以下のようになります。
売上日 | 売上番号 | 商品ID | 数量 |
---|---|---|---|
20090702 | 00001 | 1020 | 10 |
20090702 | 00002 | 1010 | 5 |
20090702 | 00003 | 3010 | 20 |
20090703 | 00001 | 1030 | 10 |
20090703 | 00002 | 1020 | 15 |
20090704 | 00001 | 2020 | 30 |
20090704 | 00002 | 3010 | 20 |
以下のコードは、売上データSALESを「五十音順で売上番号の昇順」にソートします。
# 売上データSALESをファイル/tmp/sort.inに書き込み cat /dev/null > /tmp/sort.in for (( I=0; I < ${#SALES[@]}; ++I )) do SALE=${SALES[$I]} echo $SALE >> /tmp/sort.in done # ファイル/tmp/sort.in中の売上データを五十音順で売上番号の昇順にソートし、ファイル/tmp/sort.outに出力 sort -t'|' -k2 -d /tmp/sort.in > /tmp/sort.out # ファイル/tmp/sort.outからソート後の売上データを読み込み、配列RESULTSに格納 while read LINE do RESULTS=("${RESULTS[@]}" "$LINE") done < /tmp/sort.out
ソート後の売上データRESULTSをHTMLテーブルとして出力すると、以下のようになります。
売上日 | 売上番号 | 商品ID | 数量 |
---|---|---|---|
20090702 | 00001 | 1020 | 10 |
20090703 | 00001 | 1030 | 10 |
20090704 | 00001 | 2020 | 30 |
20090702 | 00002 | 1010 | 5 |
20090703 | 00002 | 1020 | 15 |
20090704 | 00002 | 3010 | 20 |
20090702 | 00003 | 3010 | 20 |
ソートの例(数値順)
例をもう一つ挙げます。商品データPRODUCTSを特定の項目の「数値順」にソートします。
PRODUCTS=("${PRODUCTS[@]}" "2010|定規|140") PRODUCTS=("${PRODUCTS[@]}" "2020|コンパス|300") PRODUCTS=("${PRODUCTS[@]}" "3010|のり|200") PRODUCTS=("${PRODUCTS[@]}" "1030|消しゴム|100") PRODUCTS=("${PRODUCTS[@]}" "1020|ボールペン|100") PRODUCTS=("${PRODUCTS[@]}" "1010|えんぴつ|80")
商品データPRODUCTSをHTMLテーブルとして出力すると、以下のようになります。
商品ID | 商品名 | 価格 |
---|---|---|
2010 | 定規 | 140 |
2020 | コンパス | 300 |
3010 | のり | 200 |
1030 | 消しゴム | 100 |
1020 | ボールペン | 100 |
1010 | えんぴつ | 80 |
以下のコードは、商品データPRODUCTSを「数値順で商品IDの降順」にソートします。
# 商品データPRODUCTSをファイル/tmp/sort.inに書き込み cat /dev/null > /tmp/sort.in for (( I=0; I < ${#PRODUCTS[@]}; ++I )) do PRODUCT=${PRODUCTS[$I]} echo $PRODUCT >> /tmp/sort.in done # ファイル/tmp/sort.in中の商品データを数値順で商品IDの昇順にソートし、ファイル/tmp/sort.outに出力 sort -t'|' -k1 -n -r /tmp/sort.in > /tmp/sort.out # ファイル/tmp/sort.outからソート後の商品データを読み込み、配列RESULTSに格納 while read LINE do RESULTS=("${RESULTS[@]}" "$LINE") done < /tmp/sort.out
ソート後の商品データRESULTSをHTMLテーブルとして出力すると、以下のようになります。
商品ID | 商品名 | 価格 |
---|---|---|
3010 | のり | 200 |
2020 | コンパス | 300 |
2010 | 定規 | 140 |
1030 | 消しゴム | 100 |
1020 | ボールペン | 100 |
1010 | えんぴつ | 80 |
■結合
2種類のデータを、特定の条件で結合するアルゴリズムです。
例として、商品データPRODUCTSと売上データSALESを結合します。
PRODUCTS=("${PRODUCTS[@]}" "1010|えんぴつ|80") PRODUCTS=("${PRODUCTS[@]}" "1020|ボールペン|100") PRODUCTS=("${PRODUCTS[@]}" "1030|消しゴム|100") PRODUCTS=("${PRODUCTS[@]}" "2010|定規|140") PRODUCTS=("${PRODUCTS[@]}" "2020|コンパス|300") PRODUCTS=("${PRODUCTS[@]}" "3010|のり|200") SALES=("${SALES[@]}" "20090702|00001|1020|10") SALES=("${SALES[@]}" "20090702|00002|1010|5") SALES=("${SALES[@]}" "20090702|00003|3010|20") SALES=("${SALES[@]}" "20090703|00001|1030|10") SALES=("${SALES[@]}" "20090703|00002|1020|15") SALES=("${SALES[@]}" "20090704|00001|2020|30") SALES=("${SALES[@]}" "20090704|00002|3010|20")
商品データPRODUCTSをHTMLテーブルとして出力すると以下のようになります。
商品ID | 商品名 | 価格 |
---|---|---|
1010 | えんぴつ | 80 |
1020 | ボールペン | 100 |
1030 | 消しゴム | 100 |
2010 | 定規 | 140 |
2020 | コンパス | 300 |
3010 | のり | 200 |
売上日 | 売上番号 | 商品ID | 数量 |
---|---|---|---|
20090702 | 00001 | 1020 | 10 |
20090702 | 00002 | 1010 | 5 |
20090702 | 00003 | 3010 | 20 |
20090703 | 00001 | 1030 | 10 |
20090703 | 00002 | 1020 | 15 |
20090704 | 00001 | 2020 | 30 |
20090704 | 00002 | 3010 | 20 |
以下のコードは、商品データと売上データを商品IDをキーとして結合します。
# すべての売上データについて繰り返す。 for (( I=0; I < ${#SALES[@]}; ++I )) do SALE=${SALES[$I]} S_SALES_DATE=`echo $SALE | awk -F'|' '{print $1}'` S_SALES_NO=`echo $SALE | awk -F'|' '{print $2}'` S_PRODUCT_ID=`echo $SALE | awk -F'|' '{print $3}'` S_AMOUNT=`echo $SALE | awk -F'|' '{print $4}'` # すべての商品データについて繰り返す。 for (( J=0; J < ${#PRODUCTS[@]}; ++J )) do PRODUCT=${PRODUCTS[$J]} P_PRODUCT_ID=`echo $PRODUCT | awk -F'|' '{print $1}'` P_PRODUCT_NAME=`echo $PRODUCT | awk -F'|' '{print $2}'` P_PRICE=`echo $PRODUCT | awk -F'|' '{print $3}'` # 売上データと商品データの商品IDが一致することを確認する。 if [ $S_PRODUCT_ID = $P_PRODUCT_ID ]; then # 商品データと売上データの項目を結合した結果を配列RESULTSに格納する。 RESULT="$S_SALES_DATE|$S_SALES_NO|$S_PRODUCT_ID|$P_PRODUCT_NAME|$S_AMOUNT|$P_PRICE" RESULTS=("${RESULTS[@]}" "$RESULT") fi done done
結合結果RESULTSをHTMLテーブルとして出力すると、以下のようになります。
売上日 | 売上番号 | 商品ID | 商品名 | 数量 | 価格 |
---|---|---|---|---|---|
20090702 | 00001 | 1020 | ボールペン | 10 | 100 |
20090702 | 00002 | 1010 | えんぴつ | 5 | 80 |
20090702 | 00003 | 3010 | のり | 20 | 200 |
20090703 | 00001 | 1030 | 消しゴム | 10 | 100 |
20090703 | 00002 | 1020 | ボールペン | 15 | 100 |
20090704 | 00001 | 2020 | コンパス | 30 | 300 |
20090704 | 00002 | 3010 | のり | 20 | 200 |
■集計
複数のデータを特定の条件で集計するアルゴリズムです。
例として、売上データSALESを集計します。
SALES=("${SALES[@]}" "20090702|00001|ボールペン|100|10") SALES=("${SALES[@]}" "20090702|00002|えんぴつ|80|5") SALES=("${SALES[@]}" "20090702|00003|のり|200|20") SALES=("${SALES[@]}" "20090703|00001|消しゴム|100|10") SALES=("${SALES[@]}" "20090703|00002|ボールペン|100|15") SALES=("${SALES[@]}" "20090704|00001|コンパス|300|30") SALES=("${SALES[@]}" "20090704|00002|のり|200|20")
売上データSALESをHTMLテーブルとして出力すると以下のようになります。
売上日 | 商品ID | 商品名 | 価格 | 数量 |
---|---|---|---|---|
20090702 | 00001 | ボールペン | 100 | 10 |
20090702 | 00002 | えんぴつ | 80 | 5 |
20090702 | 00003 | のり | 200 | 20 |
20090703 | 00001 | 消しゴム | 100 | 10 |
20090703 | 00002 | ボールペン | 100 | 15 |
20090704 | 00001 | コンパス | 300 | 30 |
20090704 | 00002 | のり | 200 | 20 |
以下のコードは、売上データSALESを集計して売上日毎の合計数量と合計売上高を求めます。
####################################################### ## 集計結果の初期化 ####################################################### # すべての売上データについて繰り返す。 for (( I=0; I < ${#SALES[@]}; ++I )) do # 売上データの売上日を取得する。 SALE=${SALES[$I]} S_SALES_DATE=`echo $SALE | awk -F'|' '{print $1}'` # 集計結果の配列RESULTSに売上日S_SALES_DATEが含まれていないことを確認する。 FOUND=0 for (( J=0; J < ${#RESULTS[@]}; ++J )) do RESULT=${RESULTS[$J]} R_SALES_DATE=`echo $RESULT | awk -F'|' '{print $1}'` if [ $S_SALES_DATE = $R_SALES_DATE ]; then FOUND=1 break fi done if [ $FOUND = 0 ]; then # 集計結果の配列RESULTSに売上日S_SALES_DATEが含まれていない: # 集計結果の初期値を生成する。 RESULT="$S_SALES_DATE|0|0" # 生成した初期値を集計結果の配列RESULTSに格納する。 RESULTS=("${RESULTS[@]}" "$RESULT") fi done ####################################################### ## 売上データを集計 ####################################################### # すべての売上データについて繰り返す。 for (( I=0; I < ${#SALES[@]}; ++I )) do SALE=${SALES[$I]} S_SALES_DATE=`echo $SALE | awk -F'|' '{print $1}'` S_PRICE=`echo $SALE | awk -F'|' '{print $4}'` S_AMOUNT=`echo $SALE | awk -F'|' '{print $5}'` # すべての集計結果について繰り返す。 for (( J=0; J < ${#RESULTS[@]}; ++J )) do RESULT=${RESULTS[$J]} R_SALES_DATE=`echo $RESULT | awk -F'|' '{print $1}'` R_AMOUNT=`echo $RESULT | awk -F'|' '{print $2}'` R_TOTAL=`echo $RESULT | awk -F'|' '{print $3}'` # 売上日付が一致する集計結果に対し、売上データを計上する。 if [ $S_SALES_DATE = $R_SALES_DATE ]; then # 売上データの数量を集計結果に足し合わせる。 R_AMOUNT=$(( $R_AMOUNT + $S_AMOUNT )) # 売上データの価格と数量を掛けた値を集計結果に足し合わせる。 R_TOTAL=$(( $R_TOTAL + $S_PRICE * $S_AMOUNT )) RESULTS[$J]="$R_SALES_DATE|$R_AMOUNT|$R_TOTAL" break fi done done
集計結果は配列RESULTSに格納されます。集計結果RESULTSをHTMLテーブルとして出力すると以下のようになります。
売上日 | 合計数量 | 合計売上高 |
---|---|---|
20090702 | 35 | 5400 |
20090703 | 25 | 2500 |
20090704 | 50 | 13000 |
データ構造
アルゴリズム
- Javaを使うなら理解しておきたいアルゴリズム - 抽出・ソート・結合・集計 (リスト&マップ編)
- Javaを使うなら理解しておきたいアルゴリズム - 抽出・ソート・結合・集計 (リスト&ビーン編)
- PHPを使うなら理解しておきたいアルゴリズム - 抽出・ソート・結合・集計
- VBAを使うなら理解しておきたいアルゴリズム - 抽出・結合・集計
- Javascriptを使うなら理解しておきたいアルゴリズム - 抽出・ソート・結合・集計
- SQLを使うなら理解しておきたいアルゴリズム?(というか、select文の書き方) - where・order by・join・group by
- Bashを使うなら理解しておきたいアルゴリズム - 抽出・ソート・結合・集計