C ++ - 修改所有地图元素的键

时间:2016-07-02 20:35:50

标签: c++ stdmap

让我们考虑一下这段代码:

std::map< int, char > charMap;
for( auto& i : charMap )
{
    charMap[ i.first + 1 ] = charMap[ i.first ];
    charMap.erase( i.first );
}

让我们说地图有一些带有randomed键的值。我试图将键移动1。 这不会起作用,因为循环会永远持续下去。 有没有一种快速的方法让它发挥作用?

3 个答案:

答案 0 :(得分:5)

在C ++ 17中,您可以使用节点提取和拼接(参见P0083R3):

std::map<int, char> tmpMap;

for (auto it = charMap.begin(); it != charMap.end(); )
{
    auto nh = charMap.extract(it++);   // node handle
    ++nh.key();
    tmpMap.insert(tmpMap.end(), std::move(nh));
}

tmpMap.swap(charMap);

循环提取连续的映射节点,对其进行变异,然后将节点重新插入tmpMap(现在使用不同的密钥)。最后,charMap为空,tmpMap包含所有修改了密钥的元素,因此我们将两者交换。

在C ++ 17之前,您必须复制(或移动)值数据以插入带有新密钥的新元素。

std::map<int, char> tmpMap;

for (auto & p : charMap)
    tmpMap.emplace_hint(tmpMap.end(), p.first + 1, std::move(p.second));

tmpMap.swap(charMap);

这需要节点的内存分配,因此新的基于拼接的解决方案更有效。

在任何一种情况下,我们都可以使用提示插入,因为我们以相同的顺序重建元素,因此最新的元素总是插在最后。

答案 1 :(得分:3)

使用对订单的已知影响的临时解决方案

您可以简单地选择从最后一个元素开始的向后迭代:

for( auto pi = charMap.end(); pi-- != charMap.begin(); pi=charMap.erase( pi ))
    charMap[ pi->first + 1 ] = charMap[ pi->first ];

<强> Online demo

这不会永远循环,因为您插入的新元素将始终位于当前元素之后,因此不会一次又一次地重新处理。

更一般的解决方案

对于更一般的转型,您无法确定对元素排序的影响,我宁愿选择std::transform()

std::map<int, char> tmp; 
std::transform(charMap.begin(), charMap.end(), std::inserter(tmp,tmp.begin()), 
                          [](auto e) { return std::make_pair(e.first+1, e.second); });
std::swap(tmp, charMap);    // the old map will be discarded when tmp goes out of scope

<强> Online demo

答案 2 :(得分:0)

您不能使用这种范围迭代有两个基本原因:

第一个原因是地图的基本属性是迭代地图按关键顺序迭代。

您正在迭代地图。因此,如果映射中的第一个键是键0,则将值复制到键1.然后,迭代到映射中的下一个键,即刚刚创建的键1,然后将其复制到键2.泡沫,冲洗,重复。

有几种方法可以解决这个问题,但由于地图的第二个基本方面,这些都不重要:

charMap[1]=charMap[0];

这可以应对charMap[0]charMap[1]。它对charMap[0]没有任何作用。它仍然存在。什么也没发生。因此,假设地图中的最低键为0,并且您正确地移动了键,您仍然会在地图中使用键0输入一个值。同样适用于地图中的其他所有内容。

但是,让我们说你解决了可以解决的几种方法之一的第一个问题。然后,让我们说你的地图有0,5和7键的值。

将密钥#0复制到密钥#1,将密钥#5复制到密钥#6,将密钥#7复制到密钥#8后,拿一支纸和一支铅笔,找出你现在在地图中的内容。

答案:它不会是键1,6和8.它将是键0,1,5,6,7和8.

您所做的只是将每个值复制到下一个键。这是因为计算机完全按照你的要求去做,不多也不少。计算机无法按照您的意愿执行操作。

最简单的方法是创建一个新地图,并使用更新的键值将旧地图的内容复制到新地图。您仍然可以使用范围迭代。然后,用新地图替换旧地图。

当然,如果地图非常大,这就变得不切实际了。在这种情况下,仍然可以在不使用第二个映射的情况下执行此操作,但算法会有些复杂。胶囊摘要是:

1)以相反的顺序迭代键。不能在这里使用范围迭代。

2)将密钥复制到地图中的下一个值后,显式删除其原始密钥中的值。