如何从另一张地图构造一张地图

How can I construct one map from another map?

本文关键字:一张 地图      更新时间:2023-10-16

我正试图使用比较器函数从另一个映射创建一个映射,即键值对中的新值与映射中存储的键值对中以前的值不同。

我在编译以下代码时遇到编译错误。该代码有什么问题?还有更好的方法来实现这一点吗?

#include <iostream>
#include <map>
#include <set>
#include <algorithm>
#include <functional>
int main() {
// Creating & Initializing a map of String & Ints
std::map<std::string, int> mapOfWordCount = { { "aaa", 10 }, { "ddd", 41 },
{ "bbb", 62 }, { "ccc", 10} };
// Declaring the type of Predicate that accepts 2 pairs and return a bool
typedef std::function<bool(std::pair<std::string, int>, std::pair<std::string, int>)> Comparator;
// Defining a lambda function to compare two pairs. It will compare two pairs using second field
Comparator compFunctor =
[](std::pair<std::string, int> elem1 ,std::pair<std::string, int> elem2)
{
return elem1.second != elem2.second;
};
// Declaring a set that will store the pairs using above comparision logic
std::map<std::string, int, Comparator> setOfWords(
mapOfWordCount.begin(), mapOfWordCount.end(), compFunctor);
return 0;
}

第二张地图的预期输出为:

{ "aaa", 10 }
{ "ddd", 41 }
{ "bbb", 62 }

这意味着必须忽略{ "ccc", 10 }

错误摘录:

sortMap.cpp:25:70:此处为必填项/opt/tools/installs/gcc-4.8.3/include/c++/4.8.3/bits/sstl_tree.h:1422:8:错误:对的调用没有匹配项'(std::function,int>,std::pair,int>)>)(conststd::basic_string&,const key_type&)'&amp_M_impl_M_key_compare(_S_key(_M_rightmost()),__k)^在/opt/tools/installs/gcc-4.8.3/include/c++/4.8.3/bits/sstl_algo.h:66:0中包含的文件中,来自/opt/tools/installs/gcc-4.8.3/include/c++/4.8.3/algorithm:62,从sortMap.cpp:4:/opt/tools/installs/gcc-4.8.3/include/c++/4.8.3/functional:22174:11:注:候选人为:类函数&lt_Res(_ArgTypes…)>^/opt/tools/installs/gcc-4.8.3/include/c++/4.8.3/functional:2466:5:注意:_Res std::函数&lt_Res(_ArgTypes…)>::运算符()(_ArgType…)const[with _Res=bool;_ArgTypes={std::对,std::分配器>,int>,std::对,std::分配器>,int>}]函数&lt_Res(_ArgTypes…)>::^

这是一个符合OP.所述意图的解决方案

样本代码:

#include <iostream>
#include <map>
#include <set>
#include <vector>
int main()
{
// Creating & Initializing a map of String & Ints
std::map<std::string, int> mapOfWordCount = {
{ "aaa", 10 }, { "ddd", 41 }, { "bbb", 62 }, { "ccc", 10 }
};
// auxiliary set of values
std::set<int> counts;
// creating a filtered map
std::vector<std::pair<std::string, int> > mapOfWordCountFiltered;
for (const std::map<std::string, int>::value_type &entry : mapOfWordCount) {
if (!counts.insert(entry.second).second) continue; // skip duplicate counts
mapOfWordCountFiltered.push_back(entry);
}
// output
for (const std::pair<std::string, int> &entry : mapOfWordCountFiltered) {
std::cout << "{ "" << entry.first << "", " << entry.second << " }n";
}
// done
return 0;
}

输出:

{ "aaa", 10 }
{ "bbb", 62 }
{ "ddd", 41 }

coliru上的实时演示

没有使用自定义谓词,因为标准谓词(std::less<Key>)对于解决方案(对于mapset)是足够的。

过滤后的映射甚至不使用std::map,因为没有必要这样做。(条目已经排序,过滤由额外的std::set<int>完成。)

实际上,我不知道如何使用自定义谓词来执行此操作,因为我不知道怎样通过额外检查重复值来保持映射的(必需的)顺序。


如果之前对应于不同键的映射中已经存在另一个"键,值",那么是否有一种方法可以创建一个比较器来确保不插入该值?这将通过创建另一个集合来节省额外的空间。

我已经考虑了一段时间。是的,这是可能的,但我不建议将其用于生产代码。

std::map::insert()可能调用std::map::lower_bound()来查找插入点(即迭代器)。(std::map::lower_bound()将使用我们的自定义谓词。)如果返回的迭代器是end(),则在末尾插入条目。否则,该迭代器处的键将与新提供的(待插入的)键进行比较。如果相等,则将拒绝插入,否则将在其中插入新条目。

因此,要拒绝插入具有重复值的条目,谓词必须返回false,而不考虑键的比较。为此,谓词必须进行额外的检查。

为了执行这些额外的检查,谓词需要访问整个映射以及要插入的条目的值。为了解决第一个问题,谓词获得了对使用它的映射的引用。对于第二个问题,我没有更好的想法使用std::set<std::pair<std::string, int> >而不是原始的std::map<std::string, int>。由于已经涉及到自定义谓词,因此可以充分调整排序行为。

所以,这就是我得到的:

#include <iostream>
#include <map>
#include <set>
#include <vector>
typedef std::pair<std::string, int> Entry;
struct CustomLess;
typedef std::set<Entry, CustomLess> Set;
struct CustomLess {
Set &set;
CustomLess(Set &set): set(set) { }
bool operator()(const Entry &entry1, const Entry &entry2) const;
};
bool CustomLess::operator()(
const Entry &entry1, const Entry &entry2) const
{
/* check wether entry1.first already in set
* (but don't use find() as this may cause recursion)
*/
bool entry1InSet = false;
for (const Entry &entry : set) {
if ((entry1InSet = entry.first == entry1.first)) break;
}
/* If entry1 not in set check whether if could be added.
* If not any call of this predicate should return false.
*/
if (!entry1InSet) {
for (const Entry &entry : set) {
if (entry.second == entry1.second) return false;
}
}
/* check wether entry2.first already in set
* (but don't use find() as this may cause recursion)
*/
bool entry2InSet = false;
for (const Entry &entry : set) {
if ((entry2InSet = entry.first == entry2.first)) break;
}
/* If entry2 not in set check whether if could be added.
* If not any call of this predicate should return false.
*/
if (!entry2InSet) {
for (const Entry &entry : set) {
if (entry.second == entry2.second) return false;
}
}
/* fall back to regular behavior of a less predicate
* for entry1.first and entry2.first
*/
return entry1.first < entry2.first;
}
int main()
{
// Creating & Initializing a map of String & Ints
// with very specific behavior
Set mapOfWordCount({
{ "aaa", 10 }, { "ddd", 41 }, { "bbb", 62 }, { "ccc", 10 }
},
CustomLess(mapOfWordCount));
// output
for (const Entry &entry : mapOfWordCount) {
std::cout << "{ "" << entry.first << "", " << entry.second << " }n";
}
// done
return 0;
}

输出:

{ "aaa", 10 }
{ "bbb", 62 }
{ "ddd", 41 }

coliru上的实时演示

我的合作者会称之为弗兰肯斯坦解决方案,IMHO在这种情况下这就足够了。

std::map/std::set的意图通常是摊销插入()和查找()。由于CustomLess必须在整个集合上迭代两次(在最坏的情况下)才能返回值,因此这种效果可能完全丧失。(在某些情况下,迭代可能提前退出并没有多大帮助。)

所以,这是一个很好的谜题,我以某种方式解决了它,但更确切地说是为了提供一个反例。

正如@Galik在评论中提到的,代码的问题是映射的比较函数需要两个键作为参数,而不是键值对。因此,您无法访问比较器中的值。

与@Scheff类似,我也看不出有什么方法可以使使用自定义比较器的解决方案以实用或推荐的方式工作。但是,您也可以反转地图,而不是使用setvector。然后可以通过map::insert()函数执行过滤:

#include <map>
#include <string>
#include <iostream>
int main() {
// Creating & Initializing a map of String & Ints
std::map<std::string, int> mapOfWordCount = { { "aaa", 10 }, { "ddd", 41 },
{ "bbb", 62 }, { "ccc", 10} };
std::map<int, std::string> inverseMap;
for(const auto &kv : mapOfWordCount)
inverseMap.insert(make_pair(kv.second, kv.first));
for(const auto& kv : inverseMap)
std::cout << "{ "" << kv.second << "", " << kv.first << " }" << std::endl;
}

函数map::insert()仅在映射中不存在项的键时才插入该项。输出:

{"aaa",10}
{"ddd",41}
{"bbb",62}


但是,如果您要求目标映射setOfWords的类型为std::map<std::string, int>,那么您可以通过以下方式再次反转上面代码中的反转映射:

std::map<std::string, int> setOfWords;
for(const auto& kv : inverseMap)
setOfWords[kv.second] = kv.first;
for(const auto& kv : setOfWords)
std::cout << "{ "" << kv.first << "", " << kv.second << " }" << std::endl;

因此(即使这不是您的要求),setOfWords将按关键字排序。输出:

{"aaa",10}
{"bbb",62}
{"ddd",41}

Ideone 上的代码