C++ | 2つの vector から unordered_map を初期化する

C++ での主とした辞書型に unordered_map があります。
通常は key/value をセットとしたアイテムから要素を構築すると思います。

この記事では、通常の方式ではなく、 2つの vector (キー用 vector とバリュー用 vector)から unordered_map を構築する例を紹介します。

目次

key/value の vector から unordered_map を構築

pair の vector を経由して unordered_map を構築する

以下、プログラム例です。
ここでは、 vector<std::string> である kvector<int> である v を用いて、 unordered_map を構築しています。

C++
#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 と呼ばれるような操作に相当します。

そのあと、itemsbeginend を引数に取り、unordered_mapm を構築します。

コピーのオーバーヘッドを改善する

前述のプログラムで、処理の目的を達することができました。
しかし、文字列のコピーが2回実行されており、処理のオーバーヘッドがあります。

値のムーブによって改善したプログラム例を次に示します。

C++
#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 である kitems からは、文字列の情報が失われてしまうことが注意点です。
元の vector を保全したい場合は、「前述の例のとおり値コピーする」、「shared_ptr を用いて参照コピーする」などの対処が必要です。

上記の例では vintvector に対してはムーブを行っておりません。
int 型に対してはムーブができず、値コピーしかできないためです。
v に対してもムーブしたい場合は v.begin() をムーブイテレータに変換することで対処可能です。

C++23 以降でのプログラム例

C++23 になると、新設の ranges パッケージを用いることで、次のような記述ができるようです。
残念ながら動作確認はできていないですが、そのうち試してみようと思います。

C++ !!!動作確認未済!!!
#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 の参考サイトを掲載しておきます。

  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次