从对象矢量中提取元素

时间:2016-10-04 04:20:28

标签: c++ vector

给定一个对象矢量,是否有一种优雅的方式来提取其成员?我目前只是使用for循环,但如果有办法可以做到这一点会很好。例如,

class Object {

int x;
float y;

}

vector<Object> obj;
// Fill up obj

vector<int> all_x = obj.x // Won't work obviously

5 个答案:

答案 0 :(得分:4)

使用range-v3,它只是

std::vector<int> xs = objs | ranges::view::transform(&Object::x);

或只使用视图:

auto xs = objs | ranges::view::transform(&Object::x);

Demo

答案 1 :(得分:3)

由于std::vector(或者一般来说是c ++)不支持协变聚合,所以没有语法上很好的方法可以做你想要的。

如果您真的想要all_x成员x成员初始化 obj,那么您可以定义一个新的迭代器类,如下所示:

class getx_iter : public vector<Object>::iterator
{
public:
    getx_iter(const vector<Object>::iterator &iter) : vector<Object>::iterator(iter) {}
    int operator*() { return (*this)->x; }
};

Working code example

如果你可以初始化一个空vector然后填充它,std::transform和一个labmda是一个更清晰的选择(如@andars建议的那样)。

您还可以使用vector::reserve()back_inserter

来避免额外的初始化
xs.reserve(foos.size());
std::transform(foos.begin(), foos.end(), back_inserter(xs), [](Foo f){return f.x;});

另请注意,虽然xObject的私有成员且没有getter,但提取它会非常困难。

答案 2 :(得分:1)

我想不出一个非常好的方式。

另一种方法是将std::transform与lambda一起使用。

#include <vector>
#include <algorithm>


class Foo {
public:
    Foo(int x_): x(x_) {}
    int x;
};

int main() {
    std::vector<Foo> foos;
    for (int i = 0; i<10; i++) {
        foos.push_back(Foo(i));
    }

    std::vector<int> xs;
    xs.resize(foos.size());
    std::transform(foos.begin(), foos.end(), xs.begin(), [](Foo f){return f.x;});
}

答案 3 :(得分:0)

一些模板和宏魔术,它起作用:

#include <vector>
#include <algorithm>
using namespace std;


class Foo {
public:
    Foo(int x_): x(x_) {}
    int x;
};

#define GETFIELD(type, field) [](const type & obj){return obj.field;}

template<typename T,typename U, typename TMapper>
void MapVector(vector<T>& src, vector<U>& dst, TMapper mapper) {
    for (const auto& it: src) {
        dst.push_back(mapper(it));
    }
}

#define MapVectorField(src, dst, field) MapVector(src, dst, GETFIELD(decltype(src)::value_type, field))

int main() {
    vector<Foo> vFoo;
    for (int i = 0; i < 10; i++) {
        vFoo.push_back(Foo(i));
    }

    vector<int> vX;
    MapVector(vFoo, vX, GETFIELD(Foo, x));
    MapVectorField(vFoo, vX, x);

    for (int i = 0; i < vX.size(); i++) {
        printf("%d\n", vX[i]);
    }
}

当然,请记住,将宏MapVectorField命名为函数或在生产中编写using namespace std并不是很好。

答案 4 :(得分:0)

这是另一个非常易于使用的宏。它确实要求程序员知道元素的内部循环变量名称为“ e”。如果您牢记这一点,它将适用于字段或方法,也适用于普通对象或指向对象的指针。

宏很简单:

#define map(vTarget, vSource, eField) \
            for (auto e: vSource) { vTarget.push_back(eField); } 

例如,假设我们有一个名为Person的类,它具有一个字符串名称字段和一个int age字段,并且我们只想将名称提取到一个新的向量中。这样的用法示例可能是:

map(names, people, e.name);

请注意第三个参数中的“ e”。这是必需的,因为宏使用变量“ e”来迭代向量中的元素。因此,请记住您需要使用哪种语法。我相信这是4种情况:

  • e.field
  • e.method()
  • e-> field
  • e-> method()

让我们尝试一下。这是一个完整的例子。嘿,如果有人有一个更优雅的联接解决方案,我会为您所用。

#include <iostream>
#include <string>
#include <vector>

using namespace std;


#define map(vTarget, vSource, eField) \
            for (auto e: vSource) { vTarget.push_back(eField); } 



class Person {
    public:
        string name;
        int    age;

    public:
        Person(string name, int age) {
            this->name=name;
            this->age=age;
        }
        string& getName() {return name;}
        int     getAge()  {return age;}
};


string join(vector<string> vSource, const string separator) {
    string buf;
    bool first=true;

    for (string e: vSource) {
        if (first) {
            first=false;
        } else {
            buf+=separator;
        }
        buf+=e;
    }
    return buf;
}


int main(int argc, char **argv) {

// using a normal Object
    vector<Person> people;
    vector<string> names; 

    people.push_back(Person("john", 27));
    people.push_back(Person("jane", 26));


    names.clear();
    map(names, people, e.name);
    cout << join(names, ",") << endl;    

    names.clear();
    map(names, people, e.getName());
    cout << join(names, ",") << endl;    


// using a pointer to an object
    vector<Person*>  morePeople;
    morePeople.push_back(new Person("bob", 27));
    morePeople.push_back(new Person("amy", 26));

    names.clear();
    map(names, morePeople, e->name);
    cout << join(names, ",") << endl;    

    names.clear();
    map(names, morePeople, e->getName());
    cout << join(names, ",") << endl;    

}

样本输出:

john,jane
john,jane
bob,amy
bob,amy