简体   繁体   中英

How to use tolower with lambda function in map? C++

I want to change all the words stored in a map to lower case. with lambda function and transform, how should I do that?

std::map <string, int> M;
std::map<string, int> M1;

std::transform(M.begin(), M.end(), M1.begin(),
         [](pair<const string, int> const & p) { ::tolower(p.first); });

You can't edit keys in a map , so you'll have to create a new map

Something along the lines of:

  • iterate over the values in your map
  • take a copy of the key
  • transform it to lowercase
  • insert it into your result map:

Example:

#include <iostream>
#include <map>
#include <algorithm>

int main()
{
    std::map<std::string, int> map = {{ "HELLO", 1 }, { "WORLD", 2 }};

    std::cout << "before:\n";
    for (auto& kv : map)
    {
        std::cout << '\t' << kv.first << ":" << kv.second << '\n';
    }

    // create a new map with lowercase keys
    std::map<std::string, int> out;
    std::for_each(map.begin(), map.end(), [&](auto& kv)
        {
            std::string lower;
            std::transform(kv.first.begin(), kv.first.end(), std::back_inserter(lower), tolower);
            out[lower] = kv.second;
        });

    std::cout << "after:\n";
    for (auto& kv : out)
    {
        std::cout << '\t' << kv.first << ":" << kv.second << '\n';
    }

    return 0;
}

output:

before:
    HELLO:1
    WORLD:2
after:
    hello:1
    world:2

std::transform assigns the result of the functor to elements in the destination range. This means that the destination iterator must be the start of range with same size as the input range. In your example, the destination is an empty map. The transform essentially is the following loop:

std::map <string, int> M;
std::map<string, int> M1;

for(auto i=M.begin(), j=M1.begin(); i != M.end(); ++i,++j)
{
    *j = f(*i);
}

Decrementing j is illegal for empty containers, and doesn't really make sense for a map since you cannot change the key.

What you can see from this code is that your lambda is also incorrect. It should transform one (key value) pair into an object of the target type. In your case, the target type is the same.

You either have to resize the destination container before, eg by calling resize if it was a vector, or use an iterator which adapts the assignment to map::insert . The STL provides adaptors for this:

#include <map>
#include <string>
#include <cctype>
#include <iterator>
#include <algorithm>

int main() {
std::map <std::string, int> M;
std::map<std::string, int> M1;

std::transform(M.begin(), M.end(), std::inserter(M1, M1.begin()),
               [](std::pair<const std::string, int> const & p) 
{ 
    std::string lowercase;
    std::transform( p.first.begin(), p.first.end(), 
                    std::back_inserter(lowercase),
                    [](auto c) {return std::tolower(c);} );

    return std::make_pair(lowercase, p.second); 
});

return 0;
}

If you want to use exactly std::transform then you can use the following approach

#include <iostream>
#include <string>
#include <map>
#include <algorithm>
#include <cctype>
#include <iterator>
#include <utility>


int main ()
{
    std::map<std::string, int> m1 = { { "FIRST", 1 }, { "SECOND", 2 } };

    for ( const auto &p : m1 ) 
    {
        std::cout << p.first << '\t' << p.second << std::endl;
    }

    auto to_lower_case = []( const std::pair<const std::string, int> &p )
    {
        std::string t; t.reserve( p.first.size() );
        std::transform( p.first.begin(), p.first.end(),
                        std::back_inserter( t ), ::tolower );
        return std::make_pair( t, p.second );                        
    };

    std::cout << std::endl;

    std::map<std::string, int> m2;

    std::transform( m1.begin(), m1.end(), 
                    std::inserter( m2, m2.begin() ), to_lower_case );

    for ( const auto &p : m2 ) 
    {
        std::cout << p.first << '\t' << p.second << std::endl;
    }
}

The program output is

FIRST   1
SECOND  2

first   1
second  2

In the program there are used std::transform two times.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM