【问题标题】:splitting data of a std::map into multiple std::vectors将 std::map 的数据拆分为多个 std::vectors
【发布时间】:2015-01-13 17:45:39
【问题描述】:

我有两个类:InitTable 和 InitEntry。 InitTable 包含一个std::map table,它存储一个条目(汽车)的ID,以及代表该汽车的对象(InitEntry)。每个InitEntry 有3个成员变量:

  1. std::string ID
  2. double speed
  3. std::string heading

我的目标是首先将所有汽车存储在std::map table 中,然后遍历该数据结构,并尝试根据公共属性将汽车组织成集群(std::vector<InitEntry>):speed & @987654331 @。

示例:

假设我们有 6 辆汽车(ID 0 到 8)

  • car0:id:“0”,速度:22,航向“N”
  • car1:id:“1”,速度:26,航向“N”
  • car2:id:“2”,速度:28,航向“W”
  • car3:id:“3”,速度:12,航向“E”
  • car4:id:“4”,速度:10,航向“E”
  • car5:id:“5”,速度:45,航向“S”

为了简单起见,在这个阶段对我来说,仅根据航向对汽车进行分组就足够了。结果是:

std::vector clus1 = {car0, car1}
std::vector clus2 = {car2}
std::vector clus3 = {car3, car4}
std::vector clus4 = {car5}

不幸的是,我没有足够的 C++ STL 知识来理解如何在 C++ 中实现这一点。


InitTable.h:

    #include <InitEntry.h>


    class InitTable {
        public:
            InitTable();
            virtual ~InitTable();

            void clearTable();
            void addEntry(std::string ID, double speed, std::string heading);
            void deleteEntry(std::string ID);
            InitEntry* getEntry(std::string ID);


        protected:
            std::map<std::string, InitEntry*> table;
    };

InitTable.cc:

#include"InitTable.h"

InitTable::InitTable(){}


InitTable::~InitTable()
{
    clearTable();
}


void InitTable::clearTable()
{
    this->table.clear();
}

void InitTable::addEntry(std::string ID, double speed, std::string heading)
{
    InitEntry* newEntry = new InitEntry(ID, speed, heading);


    std::cout<< "server::InitTable: vehicle registered to the init table" << newEntry << endl;

    table.insert(std::make_pair(ID, newEntry));

}



void InitTable::deleteEntry(std::string ID)
{
    InitEntry* ie = getEntry(ID);
    if (ie != NULL)
    {
        table.erase(ID);
        delete ie;
    }
}

InitEntry* InitTable::getEntry(std::string ID)
{
    std::map<std::string, InitEntry*>::iterator it = table.find(ID);

    if (it != table.end())
    {
        return it->second;
    }
    else
    {
        std::cout << "such entry does not exist" << endl;
        return NULL;
    }
}

InitEntry.h:

class InitEntry {
    public:
        virtual ~InitEntry();
        InitEntry(std::string ID, double speed, std::string heading);
        std::string getID();


    protected:
        std::string sumoID;
        double speed;
        std::string heading;

};

InitEntry.cc:

#include "InitEntry.h"


InitEntry::InitEntry(std::string ID, double speed, std::string heading): ID(ID), speed(speed), heading(heading){}


InitEntry::~InitEntry(){}

std::string InitEntry::getID()
{
    return this->ID;
}

编辑 1:添加额外描述(应 @TomaszLewowski 的要求)。

是的,我的目标是将车辆分组,按标题,然后根据速度。因此,最初会有一大群车辆朝着某个方向行驶,后来需要根据速度将其分成更多的集群。假设:车辆向“北”行驶,速度:0 - 20... 速度:40 - 50...等

【问题讨论】:

  • 您能更详细地描述一下您想要实现的目标吗?例如您是否寻求“所有具有相同速度/方向的汽车”?在这种情况下,您的 InitEntry 可能不应该是一条记录,而是一个记录向量。或指向一个的引用/指针。
  • 注意:您有内存泄漏。在主容器中存储对象(不是新对象)。如果您的对象是多态的,您可以存储 std::uniique_ptr
  • std::map&lt;std::string, InitEntry*&gt; table; 为什么不直接使用std::map&lt;std::string, InitEntry&gt; 并跳过手动内存管理?
  • @TomaszLewowski 是的,你猜对了,这就是我想要实现的目标。
  • @PaulMcKenzie 感谢您的建议。正如我所说,我不是一个非常有经验的程序员,所以欢迎任何建议

标签: c++ dictionary vector stl c++98


【解决方案1】:

考虑将您的 std::map&lt;std::string, InitEntry*&gt; table; 更改为:

std::vector<InitEntry> table;

然后您需要将您的 *Entry 方法更改为:

void InitTable::addEntry(std::string ID, double speed, std::string heading)
{
    table.push_back(InitEntry(ID, speed, heading));
    std::cout<< "server::InitTable: vehicle registered to the init table" << table.back() << endl;
}

void InitTable::deleteEntry(std::string ID)
{
    auto it = remove_if(table.begin(), table.end(), [&](const auto& i){return i.getID() == ID;});
    table.resize(distance(table.begin(), it));
}

InitEntry* InitTable::getEntry(std::string ID)
{
    auto it = find_if(table.begin(), table.end(), [&](const auto& i){return i.getID() == ID;});

    if (it != table.end())
    {
        return it->second;
    }
    else
    {
        std::cout << "such entry does not exist" << endl;
        return NULL;
    }
}    

要对table 进行排序,您需要确定排序依据。

  • 按 ID 排序:std::sort(table.begin(), table.end(), [](const auto&amp; first, const auto&amp; second){return first.getID() &lt; second.getID();});
  • 按速度排序:std::sort(table.begin(), table.end(), [](const auto&amp; first, const auto&amp; second){return first.getSpeed() &lt; second.getSpeed();});
  • 按标题排序:std::sort(table.begin(), table.end(), [](const auto&amp; first, const auto&amp; second){return first.getHeading() &lt; second.getHeading();});

显然,您需要添加 getter 才能使这些工作。

【讨论】:

    【解决方案2】:

    如果您不想遍历所有元素(我猜您不想),那么唯一的选择是将这些组保留在某处,并且在主表中仅保留它们的一些索引。这大概是这样的:

    #include <string>
    #include <vector>
    #include <map>
    #include <cassert>
    #include <algorithm>
    
    typedef std::string ID;
    typedef std::string Heading;
    typedef double Speed;
    
    struct Entry
    {
        ID id;
        Speed speed;
        Heading heading;
    };
    
    class EntryProxy
    {
    public:
        EntryProxy(Entry* target) : entry(target)
        {}
    
        ID getId()
        {
            return entry->id;
        }
    
        Speed getSpeed()
        {
            return entry->speed;
        }
    
        Heading getHeading()
        {
            return entry->heading;
        }
    
    private:
        Entry* entry;
    };
    
    class InitTable
    {
    public:
        const std::vector<EntryProxy>& getSameHeading(std::string id)
        {
            return groupedByHeadings.at(entries.at(id).heading);
        }
    
        const std::vector<EntryProxy>& getSimilarSpeed(std::string id)
        {
            return groupedBySpeed.at(calculateSpeedGroupIndex(entries.at(id).speed));
        }
    
        void addEntry(ID id, Speed speed, Heading heading)
        {
            Entry e{ id, speed, heading };
            auto record = entries.insert(std::make_pair(id, e)); // pair<iterator, bool>
    
            groupedByHeadings[record.first->second.heading].push_back(EntryProxy(&record.first->second));
            groupedBySpeed[calculateSpeedGroupIndex(record.first->second.speed)].push_back(EntryProxy(&record.first->second));
        }
    
        void deleteEntry(ID id)
        {
            auto entry = entries.find(id);
            assert(entry != entries.end());
    
            auto currentEntryHeadings = groupedByHeadings[entry->second.heading];
            currentEntryHeadings.erase(std::find_if(currentEntryHeadings.begin(), currentEntryHeadings.end(), [&entry](EntryProxy p){return p.getId() == entry->second.id; }));
    
            auto currentEntrySpeeds = groupedBySpeed[calculateSpeedGroupIndex(entry->second.speed)];
            currentEntrySpeeds.erase(std::find_if(currentEntrySpeeds.begin(), currentEntrySpeeds.end(), [&entry](EntryProxy p){return p.getId() == entry->second.id; }));
    
            entries.erase(id);
        }
    
        EntryProxy getEntry(ID id)
        {
            return EntryProxy(&entries.at(id));
        }
    
    private:
        typedef int SpeedGroupIndex;
    
        SpeedGroupIndex calculateSpeedGroupIndex(Speed s)
        {
            // you may prefer a different implementation here
    
            return s / 10;
        }
    
        std::map<ID, Entry> entries;
        std::map<Heading, std::vector<EntryProxy> > groupedByHeadings;
        std::map<SpeedGroupIndex, std::vector<EntryProxy> > groupedBySpeed;
    };
    

    按标题分组非常明显,因为它只需要一个简单的地图。然而,在速度方面,它有点困难 - 您需要描述分组要求(通过实现函数 getSpeedGroupId),因为双精度不应该用作地图索引。

    如果您更喜欢更安全的内存管理方法,您可以用Entry* 代替EntryProxy 中的std::shared_ptr&lt;Entry&gt;Entry 代替InitTable::entries 中的std::shared_ptr&lt;Entry&gt;。在这种情况下,您还需要将创建从 Entry e{id, speed, heading 更改为 auto e = std::make_shared&lt;Entry&gt;(id, speed, heading)

    如果删除对您来说太复杂,您可以将 EntryProxy 结构保留在列表中,并在 entries 映射中保留对特定迭代器的引用。

    您可能也不喜欢组是 EntryProxy 而单个元素是 Entry 的事实 - 在这种情况下,您可以让 getEntry 也返回 EntryProxy

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-08-17
      • 2010-09-17
      • 2015-04-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多