简体   繁体   English

根据某些条件分割std :: vector

[英]Splitting std::vector based on some criteria

I have a vector which contain some data. 我有一个包含一些数据的向量。 I want to split it into const number of vectors depending on some criteria. 我想根据某些标准将其拆分为const数量的向量。 For example: 例如:

using Point=std::pair<int,int>;
std::array<std::vector<Point>,4> split_to_4(const std::vector<Point>& data,std::function<size_t(Point)> criteria);
int main(){
    std::vector<Point> data;
    //fill data
    auto results=split_to_4(data,[](const Point& p){
        if(cond1) return 0;
        if(cond2) return 1;
        if(cond3) return 2;
        return 3;
    });
}

What is the best way to implement split_to_4 ? 实现split_to_4的最佳方法是什么? My current attempt is: 我目前的尝试是:

std::array<std::vector<Point>,4> split_to_4(const std::vector<Point>& data,std::function<size_t(Point)> criteria){
    std::array<std::vector<Point>,4> result;
    for (const auto& p : data){
        areas_regions[criteria(p)].emplace_back(p);
    }
    return result;
}

Any better.. More std way to do it? 任何更好的。更std办法做到这一点?

By Better, I mean: more readable... depend on iterator... depend on some std functions... 更好,我的意思是:更具可读性...依赖迭代器...依赖某些std函数...

You can do this in place with multiple calls to std::partition : 您可以通过多次调用std::partition来就地执行此操作:

// Returns iterators to the three partition points in the range
template<class ForwardIt, class Which>
auto split4(ForwardIt first, ForwardIt last, Which which) {
    std::array<ForwardIt, 3> ret;
    ret[0] = std::partition(first, last, 
                [&](const auto &v){return which(v) == 0;});
    ret[1] = std::partition(ret[0], last, 
                [&](const auto &v){return which(v) == 1;});
    ret[2] = std::partition(ret[1], last,
                [&](const auto &v){return which(v) == 2;});
    return ret;
}

Of course you can also pass and use the conditions directly instead of proxiing through some which function if you so desire. 当然which如果您愿意,也可以直接传递和使用条件,而不是通过某些功能来代替。

One could also trivially rewrite this with a loop to generalize it to splitN if necessary. 如有必要,还可以用一个循环将其重写,以将其概括为splitN (Watch out though, the complexity of this approach is O(N * n) for a range with n elements. This will probably be unreasonably slow for big N. On the other hand, we get swaps instead of copies, which may help if copying is expensive (compared to calling which ). If performance is critical, measure.) (但是请注意,对于具有n个元素的范围,此方法的复杂度为O(N * n)。这对于大的N来说可能会过慢地过慢。另一方面,我们得到了交换而不是副本,如果复制非常昂贵(与调用which相比)。如果性能至关重要,请进行衡量。)

If you need the relative order of elements in each group preserved, std::stable_partition is your friend. 如果需要保留每个组中元素的相对顺序,则std::stable_partition是您的朋友。


Just noticed the C++11 tag: The above code is C++14. 刚注意到C ++ 11标签:上面的代码是C ++ 14。 For C++11 compatibility, simply change the auto s I used to the explicit types, ie use std::array<ForwardIt, 3> as the return type and const std::iterator_traits<ForwardIt>::value_type& for the lambdas. 为了实现C ++ 11兼容性,只需将我使用的auto更改为显式类型,即使用std::array<ForwardIt, 3>作为返回类型,并使用const std::iterator_traits<ForwardIt>::value_type&作为lambda。

I'll leave that as is for brevity, this last paragraph completes the answer for the pre-C++14 folks. 为了简洁起见,我将其保留下来,这最后一段为C ++ 14之前的人们提供了完整的答案。

update: 更新:

probably the most STL-like way: 可能是最类似于STL的方式:

Features: 特征:

  1. Iterator-based so choice of source and destination containers is left to the caller 基于迭代器,因此源容器和目标容器的选择留给调用方

  2. Source iterators may be move-iterators if move-partitioning is required, or leave as normal iterators to make a copy 如果需要进行移动分区,则源迭代器可以是move-iterators,也可以保留正常的迭代器以进行复制

  3. Linear time complexity 线性时间复杂度

  4. Stable ordering of results (ref std::stable_partition ) 结果的稳定排序(参考std::stable_partition

- --

#include <array>
#include <vector>
#include <utility>
#include <cassert>

using Point=std::pair<int,int>;

// example split function - could be a function object
extern std::size_t which_bucket(const Point&);


template<class Iter, class OutIter, class Which>
  auto split_n(Iter first, Iter last, 
               OutIter outfirst, std::size_t N, 
              Which&& which)
{
  while (first != last) {
    auto index = which(*first);
    assert (index < N);
    std::next(outfirst, index) -> push_back(*first);
    ++ first;
  }
}

template<class Iter, class OutIter, class Which>
  auto split_to(Iter first, Iter last, 
               OutIter outfirst, OutIter outlast, 
              Which&& which)
{
  return split_n(first, last, outfirst, 
                  std::distance(outfirst, outlast),
                  std::forward<Which>(which));
}


int main(){
  std::vector<Point> source;
  std::array<std::vector<Point>, 4> dest { }; 

  split_n(source.begin(), source.end(), 
          dest.begin(), dest.size(), 
          which_bucket);

  // or

  split_to(source.begin(), source.end(),
           dest.begin(), dest.end(),
           which_bucket);

  // or with move request:

  split_to(std::make_move_iterator(source.begin()), 
           std::make_move_iterator(source.end()),
           dest.begin(), dest.end(),
           which_bucket);
}

another way 其他方式

#include <array>
#include <vector>
#include <utility>

using Point=std::pair<int,int>;

// example split function - could be a function object
extern std::size_t which_bucket(const Point&);


template<class Iter, class Which>
  auto split4(Iter first, Iter last, Which&& which)
{
  std::array<std::vector<Point>, 4> result {};
  while (first != last) {
    result[which(*first)].push_back(*first);
    ++first;
  }
  return result;
}

int main(){
  std::vector<Point> data;

  auto results = split4(data.begin(), data.end(), which_bucket);
}

Here's another way which honours any custom allocator in the vector: 这是兑现向量中任何自定义分配器的另一种方法:

#include <array>
#include <vector>
#include <utility>

using Point=std::pair<int,int>;

// example split function - could be a function object
extern std::size_t which_bucket(const Point&);


template<class T, class A, class Which>
  auto split4(const std::vector<T,A>& v, 
              Which&& which)
{
  using vec_type = std::vector<T,A>;
  std::array<std::vector<T,A>, 4> result { 
    vec_type(v.get_allocator()),
    vec_type(v.get_allocator()),
    vec_type(v.get_allocator()),
    vec_type(v.get_allocator())
  };
  for (auto& p : v) {
    result[which(p)].push_back(p);
  }
  return result;
}

int main(){
  std::vector<Point> data;

  auto results = split4(data, which_bucket);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM