【问题标题】:how to subtract std::map elements from one to other and update it in C++如何将 std::map 元素从一个元素减去另一个元素并在 C++ 中更新它
【发布时间】:2015-06-18 12:13:22
【问题描述】:

我试图从同一个 std::map 的每个其他元素中减去 std::map 的第一个元素。我找不到任何关于它的东西。是否可以?例如:

std::map<char,int> bar;
bar['a']=11; //should be deleted from other elements
bar['b']=22;
bar['c']=33;
bar['d']=44;

std::cout << "bar contains:\n";
for (std::map<char,int>::iterator it=bar.begin(); it!=bar.end(); ++it)
{
  std::cout <<"key: " <<it->first << " Value=> " << it->second << '\n'; //can i delete here??
  //i want to delete value of a from value of b and update the value of b
}
//the new map should be look like as follows
//bar['a']=11; //or may be 0
//bar['b']=11;
//bar['c']=22;
//bar['d']=33;

有什么想法可以用 c++ 中的 map 轻松完成吗? 谢谢。

【问题讨论】:

  • 你在循环中尝试过it-&gt;second = it-&gt;second - bar['a']吗?
  • @tobi303,bar.begin()-&gt;second 会不会比假设第一个键是 'a' 更好?
  • @JonathanWakely 据我了解,地图通常并不意味着元素的任何特定顺序,因此对我来说,通过它的键而不是它在地图中的位置来识别元素似乎更自然。无论如何,这取决于它应该如何使用。我可以想象bar.begin() 更有意义的用例以及'a' 更有效的用例
  • @tobi303,不,std::map 是非常明确的,这是它的核心属性之一。排序非常具体,由第三个模板参数确定。另一方面,std::unordered_map 并不暗示任何特定的顺序(线索就在名称中!) OP 非常清楚地说“减去第一个元素”,这就是 bar.begin() 所指向的那个。
  • @JonathanWakely 好的,感谢您的澄清。尽管如此,我不会称它为“更好”,而是“更一般”(请注意,他还明确指出第一个元素实际上是一个 'a'。我知道,它很容易分裂,所以没关系......)

标签: c++ stl stdmap c++03


【解决方案1】:

C++11 解决方案

您可以为此使用std::for_each

std::for_each(std::next(bar.begin()), bar.end(), [&bar](std::pair<const char, int>& x){ x.second -= bar.begin()->second; });

例如

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

int main()
{
    std::map<char,int> bar;
    bar['a']=11;
    bar['b']=22;
    bar['c']=33;
    bar['d']=44;

    std::for_each(std::next(bar.begin()), bar.end(), [&bar](std::pair<const char, int>& x){ x.second -= bar.begin()->second; });

    std::cout << "bar contains:\n";
    for (std::map<char,int>::iterator it=bar.begin(); it!=bar.end(); ++it)
    {
        std::cout << "key: " << it->first << " Value=> " << it->second << '\n';
    }
}

输出 (working demo)

bar contains:
key: a Value=> 11
key: b Value=> 11
key: c Value=> 22
key: d Value=> 33

如果您想从自身中减去第一个元素,只需删除 std::next 调用并捕获要减去的值,因为您要修改第一个映射条目。

auto const sub = bar.begin()->second;
std::for_each(bar.begin(), bar.end(), [&sub](std::pair<const char, int>& x){ x.second -= sub; });

C++03解决方案

从所有元素中减去第一个元素除了第一个元素本身

int value = bar.begin()->second;
std::map<char, int>::iterator it = bar.begin();
std::advance(it, 1);
for (; it != bar.end(); ++it)
{
    it->second -= value;
}

包含第一个元素

int value = bar.begin()->second;
for (std::map<char, int>::iterator it = bar.begin(); it != bar.end(); ++it)
{
    it->second -= value;
}

【讨论】:

  • 如果我使用 for_each 和 std::next 编译器会显示错误,但为什么?错误说——‘next’不是‘std’的成员——尽管标题和你一样。 @CoryKramer
  • @NazmulHossain 您是否使用具有可用 C++11 功能的编译器?
  • 我使用的是 g++ (Debian 4.4.5-8) 4.4.5 版本。而且我无权更新它(我认为)! @CoryKramer。任何其他可能的使用方式。感谢您的快速回复..
  • @NazmulHossain 那么您将无法访问这些 C++11 功能。请参阅我对 C++03 方法的编辑。
  • @NazmulHossain 您还应该尝试打扰您的计算机管理员升级您的 gcc 版本,您使用的是5 years old。告诉他们 Stack Overflow 上的好人建议!
【解决方案2】:

您只需遍历地图并从每个元素中减去与“第一个”键关联的值,将结果存储回同一元素中。一种方法如下所示:

http://coliru.stacked-crooked.com/a/1bc650592027f5f3 上的实时示例

#include <iostream>
#include <map>

int main() {
    // Set up the problem...
    std::map<char, int> foo;
    foo['a'] = 11;
    foo['b'] = 22;
    foo['c'] = 33;
    foo['d'] = 44;

    // Obtain the value of the 'a' key...
    const int value = foo['a'];
    // Subtract that value from each element...
    for (auto& element : foo) {
        element.second -= value;
    }

    // Output the map to verify the result...
    for (auto& element : foo) {
        std::cout << element.first << ": " << element.second << "\n";
    }

    return 0;
}

请注意,如果您遍历整个地图,则需要存储 foo[a] 的初始值,因为在迭代减法期间您会将其归零。您可以通过使用迭代器并使用例如跳过第一个元素来避免这种情况。 std::next(foo.begin())。其他答案演示了这种技术,所以我不会在这里复制它。

【讨论】:

  • const int* value = nullptr; for (auto&amp; elt : foo) if (value) elt.second -= value; else value = &amp;elt.second;
  • 如果第一个元素的键不是'a'怎么办?
  • @JonathanWakely 我的回答使用了问题中的信息。适合其他情况的修改留给读者作为练习,他们应该有足够的能力认识到这是一个从中获取灵感的例子,而不是复制和粘贴解决他们问题的方法。我想说的是,您想要减去“与已知键关联的值”的映射比减去“与基于operator&lt; 的定义的第一个元素关联的值”要大得多。跨度>
【解决方案3】:

您可以使用标头&lt;algorithm&gt;中声明的标准算法std::for_each

如果您的编译器支持 C++ 2014,那么代码可能如下所示

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

int main() 
{
    std::map<char, int> bar;

    bar['a'] = 11;
    bar['b'] = 22;
    bar['c'] = 33;
    bar['d'] = 44;

    for ( const auto &p : bar )
    {
        std::cout << "{ " << p.first << ", " << p.second << " } ";
    }
    std::cout << std::endl;

    if ( !bar.empty() )
    {        
        std::for_each( std::next( bar.begin() ), bar.end(), 
                       [value = ( *bar.begin() ).second] ( auto &p ) { p.second -= value; } );
    }            

    for ( const auto &p : bar )
    {
        std::cout << "{ " << p.first << ", " << p.second << " } ";
    }
    std::cout << std::endl;
}    

程序输出是

{ a, 11 } { b, 22 } { c, 33 } { d, 44 } 
{ a, 11 } { b, 11 } { c, 22 } { d, 33 } 

如果您的编译器仅支持 C++ 2011,那么程序中的主循环可能如下所示

if ( !bar.empty() )
{
    auto value = ( *bar.begin() ).second;
    std::for_each( std::next( bar.begin() ), bar.end(), 
                   [value] ( std::pair<const char, int> &p ) { p.second -= value; } );
} 

同样可以使用例如基于范围的 for 循环来完成

#include <iostream>
#include <map>

int main() 
{
    std::map<char, int> bar;

    bar['a'] = 11;
    bar['b'] = 22;
    bar['c'] = 33;
    bar['d'] = 44;

    for ( const auto &p : bar )
    {
        std::cout << "{ " << p.first << ", " << p.second << " } ";
    }
    std::cout << std::endl;

    if ( !bar.empty() )
    {        
        auto value = ( *bar.begin() ).second;
        bool first = true;
        for ( auto &p : bar )
        {
            if ( first ) first = !first;
            else p.second -= value;
        }
    }

    for ( const auto &p : bar )
    {
        std::cout << "{ " << p.first << ", " << p.second << " } ";
    }
    std::cout << std::endl;
}    

此外,如果应该减少所有其他元素的元素不是地图的第一个元素,那么您可以使用以下方法

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

int main() 
{
    std::map<char, int> bar;

    bar['a'] = 11;
    bar['b'] = 22;
    bar['c'] = 33;
    bar['d'] = 44;

    for ( const auto &p : bar )
    {
        std::cout << "{ " << p.first << ", " << p.second << " } ";
    }
    std::cout << std::endl;

    if ( !bar.empty() )
    {
        // the initializer can be any element not only the first one          
        const auto &value = *bar.begin(); 
        for ( auto &p : bar )
        {
            if ( p.first != value.first  ) p.second -= value.second;
        }
    }

    for ( const auto &p : bar )
    {
        std::cout << "{ " << p.first << ", " << p.second << " } ";
    }
    std::cout << std::endl;
}    

【讨论】:

  • bar.begin()-&gt;second 而不是( *bar.begin() ).second 有什么问题?如果 Stepanov 不希望我们使用 operator-&gt;,他就不会给我们。
  • @Jonathan Wakely 它们是相同的两种形式。你可以选择任何你喜欢的。
【解决方案4】:
int value = bar.begin()->second;

for(std::map<char,int>::reverse_iterator it=bar.rbegin(); it != bar.rend(); it++) {
    it->second -= bar.begin()->second;
}
bar.begin()->second = value;

【讨论】:

  • 使用您的it=bar.rbegin(); it != bar.rend(); it++),您从包括第一个元素在内的所有元素中减去(即将其值设置为 0),但问题要求“从每个 其他元素”(突出显示我的)。
  • 是的,在问题中,说 //or 可能是 0,所以我不只是不让 begin() 为 0
猜你喜欢
  • 2020-02-19
  • 1970-01-01
  • 2019-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-19
相关资源
最近更新 更多