C++ での主とした辞書型に unordered_map
があります。
通常は key/value をセットとしたアイテムから要素を構築すると思います。
この記事では、通常の方式ではなく、 2つの vector
(キー用 vector とバリュー用 vector)から unordered_map
を構築する例を紹介します。
key/value の vector から unordered_map を構築
pair の vector を経由して unordered_map を構築する
以下、プログラム例です。
ここでは、 vector<std::string>
である k
とvector<int>
である v
を用いて、 unordered_map
を構築しています。
#include <algorithm>
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
int main() {
std::vector<std::string> k{"a", "b", "c"};
std::vector<int> v{1, 2, 3};
std::vector<std::pair<std::string, int>> items;
std::transform(
k.begin(), k.end(), v.begin(), std::back_inserter(items), [](std::string k, int v) {
return std::make_pair(k, v);
});
std::unordered_map<std::string, int> m{items.begin(), items.end()};
for (const auto &[k, v] : m) {
std::cout << k << ", " << v << std::endl;
}
// Output
// c, 3
// b, 2
// a, 1
return 0;
}
unordered_map
には、vector<pair>
のイテレータを引数にとるコンストラクタがあります。
まず k
, v
をくっつけて、 vector<pair>
である items
を構築します。
python 等で zip と呼ばれるような操作に相当します。
そのあと、items
の begin
と end
を引数に取り、unordered_map
の m
を構築します。
コピーのオーバーヘッドを改善する
前述のプログラムで、処理の目的を達することができました。
しかし、文字列のコピーが2回実行されており、処理のオーバーヘッドがあります。
値のムーブによって改善したプログラム例を次に示します。
#include <algorithm>
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
int main() {
std::vector<std::string> k{"a", "b", "c"};
std::vector<int> v{1, 2, 3};
std::vector<std::pair<std::string, int>> items;
std::transform(
std::make_move_iterator(k.begin()),
std::make_move_iterator(k.end()),
v.begin(),
std::back_inserter(items),
[](std::string k, int v) { return std::make_pair(k, v); });
std::unordered_map<std::string, int> m{
std::make_move_iterator(items.begin()), std::make_move_iterator(items.end())};
for (const auto &[k, v] : m) {
std::cout << k << ", " << v << std::endl;
}
// Output
// c, 3
// b, 2
// a, 1
return 0;
}
ムーブイテレータを用いることによって、コピーのオーバーヘッドを抑制することができます。
ただし、ムーブ元となる vector
である k
と items
からは、文字列の情報が失われてしまうことが注意点です。
元の vector
を保全したい場合は、「前述の例のとおり値コピーする」、「shared_ptr
を用いて参照コピーする」などの対処が必要です。
上記の例では v
の int
型 vector
に対してはムーブを行っておりません。int
型に対してはムーブができず、値コピーしかできないためです。v
に対してもムーブしたい場合は v.begin()
をムーブイテレータに変換することで対処可能です。
C++23 以降でのプログラム例
C++23 になると、新設の ranges
パッケージを用いることで、次のような記述ができるようです。
残念ながら動作確認はできていないですが、そのうち試してみようと思います。
#include <print>
#include <ranges>
#include <string>
#include <unordered_map>
#include <vector>
int main() {
std::vector<std::string> k{"a", "b", "c"};
std::vector<int> v{1, 2, 3};
auto m = std::views::zip(k, v) | std::ranges::to<std::unordered_map<std::string, int>>();
std::println("{}", m);
return 0;
}
以下に views::zip
の参考サイトを掲載しておきます。
コメント