2012-06-13 2 views
3

Я хочу добавить две карты вместе со следующим поведением.Добавление двух карт вместе

Если ключ существует -> добавьте два ключевых значения вместе.

Если ключ не существует -> Вставьте пару в карту.

Я рассмотрел несколько стандартных алгоритмов библиотеки. А именно преобразование, но, похоже, не делает того, что я хочу.

Взятые из этого LINK

template < class InputIterator, class OutputIterator, class UnaryOperator > 
    OutputIterator transform (InputIterator first1, InputIterator last1, 
          OutputIterator result, UnaryOperator op) 
{ 
    while (first1 != last1) 
    *result++ = op(*first1++); // or: *result++=binary_op(*first1++,*first2++); 
    return result; 
} 

Мои мысли из этого, где я бы только один итератор из моей второй карты при использовании и соответствующий функтор

*result++=binary_op(*first1++,*first2++); 

Поэтому я не буду в состоянии чтобы перебрать вторую карту, чтобы найти значение ключа.

Одна мысль заключалась только в том, чтобы сделать мой собственный алгоритм с тонким изменением.

template < class InputIterator, class ContainerType, class BinaryOperator > 
    void myTransform (InputIterator first1, InputIterator last1, 
           ContainerType cont2, 
           BinaryOperator binary_op) 
{ 
    while (first1 != last1) 
    binary_op(first1++, cont2); //cont2 passed by reference 
} 

Я бы тогда быть в состоянии использовать:

cont2.find() в моем функторе искать всю карту.

Здесь был бы более полный пример того, что я думал, но я, кажется, получаю компиляционную ошибку, которую я не могу решить (я вроде догадываюсь, что тип BinaryOperator ... см. Ниже)?

#include <map> 
#include <string> 
#include <iostream> 

template < class InputIterator, class ContainerType, class BinaryOperator > 
void myTransform (InputIterator first1, InputIterator last1, 
       ContainerType &cont2, 
       BinaryOperator binary_op) 
{ 
    while (first1 != last1) 
    binary_op(first1++, cont2); //cont2 passed by reference 
} 

template<class IteratorType, class ContainerType> 
struct AddMapValues: 
    std::binary_function<IteratorType, ContainerType, void> 
{ 
    void operator()(IteratorType itr, ContainerType& cont) 
    { 
    if(cont.find(itr->first) != cont.end()) cont[itr->first] = cont.find(itr->first).second + itr->second; 
    else cont.insert((*itr)); 
    } 
}; 


int main() 
{ 
    typedef std::map<std::string, double> stringDoubleMap; 
    typedef std::map<std::string, double>::iterator stringDoubleMapItr; 
    typedef void (*ptrfnt)(stringDoubleMapItr, stringDoubleMap&); 

    stringDoubleMap map1; 
    stringDoubleMap map2; 

    map1.insert(stringDoubleMap::value_type("Test1",1.0)); 
    map1.insert(stringDoubleMap::value_type("Test2",2.0)); 
    map1.insert(stringDoubleMap::value_type("Test3",3.0)); 

    map2.insert(stringDoubleMap::value_type("Test1",1.0)); 
    map2.insert(stringDoubleMap::value_type("Test2",2.0)); 
    map2.insert(stringDoubleMap::value_type("Test3",3.0)); 

    myTransform(map1.begin(), map1.end(), 
       map2, 
       AddMapValues< stringDoubleMapItr, stringDoubleMap >()); 

    return 0; 

} 

Вот моя ошибка компилятора:

testingMapTransforms.cxx: In function ‘int main()’: 

testingMapTransforms.cxx:52:85: error: no matching function for call to  ‘myTransform(std::map<std::basic_string<char>, double>::iterator, std::map<std::basic_string<char>, double>::iterator, stringDoubleMap&, std::map<std::basic_string<char>, double>::iterator, AddMapValues<std::_Rb_tree_iterator<std::pair<const std::basic_string<char>, double> >, std::map<std::basic_string<char>, double> >)’ 

testingMapTransforms.cxx:52:85: note: candidate is: 

testingMapTransforms.cxx:12:20: note: template<class InputIterator, class ContainerType, class OutputIterator, class BinaryOperator> OutputIterator myTransform(InputIterator, InputIterator, ContainerType, OutputIterator, BinaryOperator) 

Там, кажется, еще один итератора приходит откуда-то и тип контейнера не правильно читать?

Любые идеи?

Я использую

GCC - Проект GNU C и компилятор C++

с

Ubuntu/Linaro 4.6.3-1ubuntu5

Благодарности

ПРИМЕЧАНИЕ:

Я обновил рабочую версию кода выше в ответ. Если вы думаете, что я должен просто изменить код вопроса, то дайте мне знать. Не уверен в лучшей практике

+3

Есть ли причина, по которой вы не можете просто сделать это вручную? –

+4

Я думаю, что вы делаете много операций в одной строке; '* result ++ = op (* first1 ++);' такой код трудно отлаживать, и он не очень читабельен. – Spo1ler

+0

@ Spo1ler полностью согласен. пусть компилятор выполнит свою работу по оптимизации и вместо этого код, чтобы другие люди могли его прочитать. –

ответ

0

Вот пересмотренная версия выше. В коде вопросов было много ошибок, но это похоже на работу и та же идея:

template < class InputIterator, class ContainerType, class BinaryOperator > 
void myTransform (InputIterator first1, InputIterator last1, ContainerType &cont2, BinaryOperator binary_op) 
{ 
    while (first1 != last1) 
    (*binary_op)(first1++, cont2); //cont2 passed by reference 
} 

template<class IteratorType, class ContainerType> 
struct AddMapValues: 
    std::binary_function<IteratorType, ContainerType, void> 
{ 
    static void Add(IteratorType itr, ContainerType& cont) 
    { 
    cont[itr->first] += itr->second; 
    } 
}; 

int main() 
{ 

    typedef std::map<std::string, double> stringDoubleMap; 
    typedef std::map<std::string, double>::iterator stringDoubleMapItr; 
    typedef void (*ptrfnt)(stringDoubleMapItr, stringDoubleMap&); 

    stringDoubleMap map1; 
    stringDoubleMap map2; 

    map1.insert(stringDoubleMap::value_type("Test1",1.0)); 
    map1.insert(stringDoubleMap::value_type("Test2",2.0)); 
    map1.insert(stringDoubleMap::value_type("Test4",3.0)); 

    map2.insert(stringDoubleMap::value_type("Test1",1.0)); 
    map2.insert(stringDoubleMap::value_type("Test2",10.0)); 
    map2.insert(stringDoubleMap::value_type("Test3",3.0)); 

    myTransform(map1.begin(), map1.end(), map2, AddMapValues<stringDoubleMapItr, stringDoubleMap >::Add); 

    for(stringDoubleMapItr itr = map2.begin(); itr != map2.end(); ++itr){ 
    std::cout << "INFO: Key: " << itr->first << " | Value: " << itr->second << std::endl; 
    } 

    return 0; 

} 
0

Ваш код не читается. Это склонность к ошибкам.

Сделайте это вручную, но делайте одно за раз. Не смешивайте инкрементные указатели, разыгрывая их, добавляя элементы к карте и запрашивая из другой все в одной строке. Это заканчивается беспорядком.

+0

Я согласен, что этот код не является наиболее читаемым, но я взял базу кода из преобразования из стандартной библиотеки, поэтому мои мысли состояли в том, что у этого была бы лучшая практика кодирования для алгоритма преобразования. В любом случае моя ошибка компиляции возникает из моего вызова шаблона. – MWright

+0

@MWright: Правила для реализации и правила для кода пользователя различны. – Puppy

11
I want to add two maps together with the following behavior: 
If key exists add two key values together. 
If key does not exist. Insert pair to map. 

Я думаю, что простой цикл будет делать то, что вы хотите:

for(auto it = map2.begin(); it != map2.end(); ++it) map1[it->first] += it->second; 

Если ключ существует, то значение будет добавлено к существующему. Если ключ не существует, operator[] вставляет его, и его значение будет инициализировано по умолчанию (0.0 для double).

Я не думаю, что для универсальной функции, которая будет работать на любом контейнере, здесь было бы разумно. Семантика слова vector и map insert() и оператора [] слишком различна.

+0

+1 Это умно. –

+0

@ jrok идея шаблона myTransform будет использоваться для любой карты или вектора и будет использовать любой функтор, который вы ему предоставляете. функтор AddMapValues, очевидно, будет только для карт. – MWright

1

Я не думаю, что вы можете сделать это с помощью transform. У вас есть два контейнера, вам понадобятся две пары итераторов, и вам нужно будет продвигать их отдельно, когда элемент находится только в одном из них. Две последовательности версии transform продвигают их в замке.

Это не должно быть слишком сложно сделать вручную. Что-то вроде следующего, возможно:

typedef std::map<std::string, std::double> Map 

Map 
addValues(Map const& m1, Map const& m2) 
{ 
    Map results; 
    Map::const_iterator i1 = m1.begin(); 
    Map::const_iterator i2 = m2.begin(); 
    while (i1 != m1.end() && i2 != m2.end()) { 
     if (i1->first < i2->first) { 
      results.insert(results.end(), *i1); 
      ++ i1; 
     } else if (i2->first < i1->first) { 
      results.insert(results.end(), *i2); 
      ++ i2; 
     } else { 
      results.insert(results.end(), 
          Map::value_type(i1->first, i1->second + i2->second)); 
      ++ i1; 
      ++ i2; 
     } 
    } 
    results.insert(i1, m1.end()); 
    results.insert(i2, m2.end()); 
    return results; 
} 

(я бы заставить его работать, как это первое, прежде чем сделать это шаблон.)

Смежные вопросы