如何仅针对键的子集有效地比较 C++ 中的两个字符串映射

Posted

技术标签:

【中文标题】如何仅针对键的子集有效地比较 C++ 中的两个字符串映射【英文标题】:How to efficiently compare two maps of strings in C++ only for a subset of the keys 【发布时间】:2012-01-18 08:52:34 【问题描述】:

我想知道是否只有通过应用一些标准算法才能编写一个短函数来比较两个 std::map<string, string> 并在所有键值对(但有些)为真时返回真。

例如,这两个映射应该被评估为相等

map<string,string> m1, m2;

m1["A"]="1";
m2["A"]="1";

m1["B"]="2";
m2["B"]="2";

m1["X"]="30";
m2["X"]="340";

m1["Y"]="53";
m2["Y"]="0";

假设两个映射具有相同的大小,并且它们的所有元素都必须成对比较,除了键“X”和键“Y”存储的值。第一次尝试将是一个非常低效的双嵌套 for 循环。 我确信可以实现更好的解决方案。

【问题讨论】:

您对两张地图相等(不相等)的含义的定义是模棱两可的。除了一些,你是什么意思?至少应该有多少,一个? 相等的意思是“相同数量的元素和相同的键,忽略值”? 如果要检查两张图是否完全相等,== 应该足够了。 对不起,我不是很清楚。假设这两个映射有 N 个元素,但我只想比较 M m1 和 m2 中是相等的,欢迎使用模板化解决方案......跨度> @Als,已声明使用 std::equal,您可以在其中指定要检查的元素范围,这样就足够了。 【参考方案1】:

我认为,如果您尝试同时访问两个大小相同的地图,那么您应该创建迭代器,并以这种方式遍历两个地图。

 map<char, int> :: iterator itr1, itr2;
 itr1=first.begin();
 itr2=last.begin();
 
 while(itr1!=first.end() && itr2!=last.end())
 
     if(itr1->first!=itr2->first || itr1->second!=itr2->second)
     
       cout<<"Error"<<endl;
     
     else 
     
       cout<<"Ok so far"<<endl;
     
     itr1++;
     itr2++;
 

【讨论】:

打印出贡献者姓名的解决方案可以被视为垃圾邮件。此外,即使它本质上是正确的,但除了公认的答案之外,它什么也没给出。 当有可用于此目的的标准算法时,这是一个非常糟糕的技术解决方案。【参考方案2】:

我不确定您到底在寻找什么,所以让我先给出完全相等,然后再给出关键相等。也许后者已经满足您的需求了。

完全相等

(虽然可以使用std::map 自己的比较运算符来测试标准等价性,但以下可以用作基于每个值进行比较的基础。)

完全相等可以使用std::equalstd::operator== 测试std::pairs:

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

template <typename Map>
bool map_compare (Map const &lhs, Map const &rhs) 
    // No predicate needed because there is operator== for pairs already.
    return lhs.size() == rhs.size()
        && std::equal(lhs.begin(), lhs.end(),
                      rhs.begin());


int main () 
    using namespace std;

    map<string,string> a, b;

    a["Foo"] = "0";
    a["Bar"] = "1";
    a["Frob"] = "2";

    b["Foo"] = "0";
    b["Bar"] = "1";
    b["Frob"] = "2";

    cout << "a == b? " << map_compare (a,b) << " (should be 1)\n";
    b["Foo"] = "1";
    cout << "a == b? " << map_compare (a,b) << " (should be 0)\n";

    map<string,string> c;
    cout << "a == c? " << map_compare (a,c)  << " (should be 0)\n";

关键平等

C++2003

基于以上代码,我们可以在std::equal调用中添加谓词:

struct Pair_First_Equal 
    template <typename Pair>
    bool operator() (Pair const &lhs, Pair const &rhs) const 
        return lhs.first == rhs.first;
    
;

template <typename Map>
bool key_compare (Map const &lhs, Map const &rhs) 
    return lhs.size() == rhs.size()
        && std::equal(lhs.begin(), lhs.end(),
                      rhs.begin(),
                      Pair_First_Equal()); // predicate instance


int main () 
    using namespace std;

    map<string,string> a, b;

    a["Foo"] = "0";
    a["Bar"] = "1";
    a["Frob"] = "2";

    b["Foo"] = "0";
    b["Bar"] = "1";
    b["Frob"] = "2";

    cout << "a == b? " << key_compare (a,b) << " (should be 1)\n";
    b["Foo"] = "1";
    cout << "a == b? " << key_compare (a,b) << " (should be 1)\n";

    map<string,string> c;
    cout << "a == c? " << key_compare (a,c)  << " (should be 0)\n";

C++ (C++11)

使用新的 lambda 表达式,您可以这样做:

template <typename Map>
bool key_compare (Map const &lhs, Map const &rhs) 

    auto pred = [] (decltype(*lhs.begin()) a, decltype(a) b)
                    return a.first == b.first; ;

    return lhs.size() == rhs.size()
        && std::equal(lhs.begin(), lhs.end(), rhs.begin(), pred);

C++ (C++14)

于 2014 年 3 月 12 日添加

使用新的通用 lambda 表达式,您可以这样做:

template <typename Map>
bool key_compare (Map const &lhs, Map const &rhs) 

    auto pred = [] (auto a, auto b)
                    return a.first == b.first; ;

    return lhs.size() == rhs.size()
        && std::equal(lhs.begin(), lhs.end(), rhs.begin(), pred);

作为样式问题,您还可以直接将 C++11 和 C++14 中的 lambda 表达式作为参数内联:

bool key_compare (Map const &lhs, Map const &rhs) 
    return lhs.size() == rhs.size()
        && std::equal(lhs.begin(), lhs.end(), rhs.begin(), 
                      [] (auto a, auto b)  return a.first == b.first; );

【讨论】:

也许也是一个键值相等,通过比较lhs.second == rhs.second这是一个非常完整的答案,谢谢! 也可以使用std::equal 变体自动检查两张地图的长度是否相同。 如果我错了,请纠正我,但这不是比较地图的有效方法。建议的比较是类向量比较。我的意思是成员(对)可能以不同的顺序排列,但地图将是平等的。 @luizfls:感谢您的编辑***.com/revisions/8473603/4,但在这种情况下它是无效的,因为提问者想要自定义比较。此外,您删除的代码作为其余答案的基础 - 请在尝试编辑现有答案之前更仔细地阅读问题。并且请在编辑之前阅读整个答案。只是一味地爆破文字片段一点都不好。 std::equal-based 解决方案不能用于无序容器(std::unordered_mapstd::unordered_set 等)。为了更好的安全性,key_compare 模板应该包含相应的static_assert 以确保它只能用于允许的容器。否则很容易产生无效代码。

以上是关于如何仅针对键的子集有效地比较 C++ 中的两个字符串映射的主要内容,如果未能解决你的问题,请参考以下文章

C++ - 如何有效地找出向量中的任何字符串是不是可以由一组字母组合而成

如何简单地比较 C++ 中的字符?

按单个字符比较两个字符串 C++

Python - 如何有效地遍历字典的子集?

降低在 C++ 中比较单个字符串中的两个字符的时间复杂度 [关闭]

如何在处理负零时有效地比较两个浮点值的符号