CArrayを二つのキーでソートする(VC++ MFC) | tomiのブログ

tomiのブログ

ブログの説明を入力します。

今、家電のリモコンが出す赤外線のデータをarduinoで拾って、PCにデータを送って、それをCSVファイルにして保存しようとしてます。

データ量は多くないし、修正はエディターでできるように出力ファイルもCSV形式にしたのですが、ボタン一発でソートする機能を付けたくなってソートロジックを調べました。

 

MFCのCArrayは便利なので、よく使っています。CArrayのなかにstructを追加でストアしていって、必要な時点でボタン一発でソートします。

 

例えば、以下の struct と CArray。

ソートキーは kisyuID と buttonID の二つ。

 

■以下はロジック確認用の実験プログラム

頭に0を入れてるデータの例

ソートロジック「qsort 1」 はネットで見つけた2項目のサンプルからCArray, CString のソートに合わせて修正したもの。

「ソートパラメータ」は「CStringでソート」。

実行させると、下のようにソートされました。

 

文字列を int にしてからクイックソートした場合。

頭に0を入れて桁数をそろえてあるので結果は同じ。

 

ロジックは以下のとおり。

■文字列のまま(CString)クイックソート


// KisyuID列の比較関数(CString編)
int cmp_Kisyu_CString(DATA* a, DATA* b)
{
	DATA* pA = (DATA*)a;
	DATA* pB = (DATA*)b;
	return (pA->kisyuID.Compare(pB->kisyuID));
}

// ButtonID列の比較関数(CString編)
int cmp_Button_CString(DATA* a, DATA* b)
{
	DATA* pA = (DATA*)a;
	DATA* pB = (DATA*)b;
	return (pA->buttonID.Compare(pB->buttonID));
}

// 比較関数(CString編)
int cmp_CString(void* context, const void* item1, const void* item2)
{
	// 比較関数リスト
	// 比較対象列分の比較関数を作成し、設定する。
	int(*func[])(DATA * pa, DATA * pb) =
                       { &cmp_Kisyu_CString , &cmp_Button_CString };
	DATA* pItem1 = (DATA*)item1;
	DATA* pItem2 = (DATA*)item2;

	for (int i = 0; i < sizeof(func) / sizeof(func[0]); i++)
	{  // 比較対象列分繰り返す
		int ret = func[i](pItem1, pItem2);	// 比較関数実行
		if (ret != 0)  // 前後が決定したか
		{
			return ret;
		}
	}

	// 全ての比較関数を実行しても前後関係が決定しないので、
	// 同等なデータと判断
	return 0;
}

qsort_s((void*)&aryData[0], aryData.GetSize(), sizeof(DATA), cmp_CString, NULL);

■文字列をintに変換してからクイックソート


// KisyuID列の比較関数(intに変換編)
int cmp_Kisyu_int(DATA* a, DATA* b)
{
	int n1 = _ttoi(a->kisyuID);
	int n2 = _ttoi(b->kisyuID);

	int wk3;
	if (n1 > n2) wk3 = 1;
	else if (n1 < n2) wk3 = -1;
	else wk3 = 0;
	return wk3;
}

// ButtonID列の比較関数(intに変換編)
int cmp_Button_int(DATA* a, DATA* b)
{
	int n1 = _ttoi(a->buttonID);
	int n2 = _ttoi(b->buttonID);

	int wk3;
	if (n1 > n2) wk3 = 1;
	else if (n1 < n2) wk3 = -1;
	else wk3 = 0;
	return wk3;
}

// 比較関数(intに変換編)
int cmp_int(void* context, const void* item1, const void* item2)
{
	// 比較関数リスト
	// 比較対象列分の比較関数を作成し、設定する。
	int(*func[])(DATA * pa, DATA * pb) = { &cmp_Kisyu_int , &cmp_Button_int };

	DATA* pItem1 = (DATA*)item1;
	DATA* pItem2 = (DATA*)item2;

	for (int i = 0; i < sizeof(func) / sizeof(func[0]); i++)
	{  // 比較対象列分繰り返す
		int ret = func[i](pItem1, pItem2);  // 比較関数実行
		if (ret != 0)  // 前後が決定したか
		{
			return ret;
		}
	}

	// 全ての比較関数を実行しても前後関係が決定しないので、
	// 同等なデータと判断
	return 0;
}

qsort_s((void*)&aryData[0], aryData.GetSize(), sizeof(DATA), cmp_int, NULL);

■比較関数ロジックを少しわかり易く書き方を変えると


//ロジック書き換えの比較関数(CString編)
int cmp_2_CString(void* context, const void* a, const void* b)
{
	int ret = 0;
	DATA* pA = (DATA*)a;
	DATA* pB = (DATA*)b;

	ret = cmp_Kisyu_CString(pA, pB);  // 比較関数実行
	if (ret != 0)  // 前後が決定したか
	{
		return ret;
	}

	ret = cmp_Button_CString(pA, pB);  // 比較関数実行
	if (ret != 0)  // 前後が決定したか
	{
		return ret;
	}

	// 全ての比較関数を実行しても前後関係が決定しないので、
	// 同等なデータと判断
	return 0;
}

qsort_s((void*)&aryData[0], aryData.GetSize(), sizeof(DATA), cmp_2_CString, NULL);

//ロジック書き換えの比較関数(intに変換編)
int cmp_2_int(void* context, const void* a, const void* b)
{
	int ret = 0;
	DATA* pA = (DATA*)a;
	DATA* pB = (DATA*)b;

	ret = cmp_Kisyu_int(pA, pB);  // 比較関数実行
	if (ret != 0)  // 前後が決定したか
	{
		return ret;
	}

	ret = cmp_Button_int(pA, pB);  // 比較関数実行
	if (ret != 0)  // 前後が決定したか
	{
		return ret;
	}

	// 全ての比較関数を実行しても前後関係が決定しないので、
	// 同等なデータと判断
	return 0;
}

qsort_s((void*)&aryData[0], aryData.GetSize(), sizeof(DATA), cmp_2_int, NULL);

書き換えた qsort のロジックでも正しくソートできている。

■バブルソートでは

バブルソートのロジックは以下のとおり。

バブルソート(CString変換編)


void bubble_sort_button_CString(CArray & aryData, int n)
{
	int ret = 0;
	for (int i = 0; i < n - 1; i++)
	{
		for (int j = 0; j < n - i - 1; j++)
		{
			ret = aryData[j].buttonID.Compare(aryData[j + 1].buttonID);

			if (ret == 1)  // [j] > [j + 1] だった
			{
				swap(aryData[j], aryData[j + 1]);
				// 要素へのポインタを渡してスワップ
			}
		}
	}
}

void bubble_sort_kisyu_CString(CArray & aryData, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		int ret = 0;
		for (int j = 0; j < n - i - 1; j++)
		{
			ret = aryData[j].kisyuID.Compare(aryData[j + 1].kisyuID);

			if (ret == 1)  // [j] > [j + 1] だった
			{
				swap(aryData[j], aryData[j + 1]);
				// 要素へのポインタを渡してスワップ
			}
		}
	}
}

bubble_sort_button_CString(aryData, aryData.GetSize());
bubble_sort_kisyu_CString(aryData, aryData.GetSize());

バブルソート(intに変換編)


void bubble_sort_button_int(CArray &aryData, int n)
{
	int a = 0;
	int b = 0;

	for (int i = 0; i < n - 1; i++)
	{
		for (int j = 0; j < n - i - 1; j++)
		{
			a = _ttoi(aryData[j].buttonID);
			b = _ttoi(aryData[j + 1].buttonID);

			if (a > b)  // 右隣が小さければ入れ替える
			{
				swap(aryData[j], aryData[j + 1]);
				// 要素へのポインタを渡してスワップ
			}
		}
	}
}

void bubble_sort_kisyu_int(CArray &aryData, int n)
{
	int a = 0;
	int b = 0;

	for (int i = 0; i < n - 1; i++)
	{
		for (int j = 0; j < n - i - 1; j++)
		{
			a = _ttoi(aryData[j].kisyuID);
			b = _ttoi(aryData[j + 1].kisyuID);

			if (a > b)  // 右隣が小さければ入れ替える
			{
				swap(aryData[j], aryData[j + 1]);
				// 要素へのポインタを渡してスワップ
			}
		}
	}
}

bubble_sort_button_int(aryData, aryData.GetSize());
bubble_sort_kisyu_int(aryData, aryData.GetSize());

■ソートするキーデータの桁が不揃いの場合

■CStringのままではソートできない

■intに変換してからソートする

以上