Home > OS >  Is there a way to iterate through a vector two items at a time in C ?
Is there a way to iterate through a vector two items at a time in C ?

Time:11-17

Imagine I have an ordered std::vector A = {x1, x2, ..., xn} and I want to perform an operation on every subsequent pair of items, e.g. f(x1, x2); f(x2, x3); ... f(xn-1, xn); f(xn, x1).

I could iterate like I normally would, while tracking the previous item:

for (auto x : A) {
    ...
    f(previous_x, x);
    previous_x = x;
}

f(previous_x, first_x);

But is there a better way to iterate through this vector? Are there features in the language that can streamline this?

Tried the solution provided. It works, but curious to know if there is a cleaner and more concise way.

CodePudding user response:

Here you are

    std::vector<int> v = { 1, 2, 3, 4, 5 };

    for (std::vector<int>::size_type i = 0, n = std::size( v ); i < n; i  )
    {
        std::cout << v[i]   v[( i   1 ) % n] << ' ';
    }
    std::cout << '\n';

the output of this code snippet is

3 5 7 9 6

You can use a similar approach.

Before the for loop you can check whether a vector contains at least two elements.

CodePudding user response:

You could use an old-school non range based for-loop and dereference the current next modulus A.size() iterator:

#include <iostream>
#include <vector>

void foo(int a, int b) { std::cout << a << ',' << b << '\n'; }

int main() {
    std::vector<int> A{1, 2, 3};

    if (A.size() >= 2) {
        for (auto it = A.begin(); it != A.end();   it) {
            foo(*it, *std::next(A.begin(),
                                (std::distance(A.begin(), it)   1) % A.size()));
        }
    }
}

Output:

1,2
2,3
3,1

Or... use indices to do the same thing, like @Vlad showed.

CodePudding user response:

Ranges provide a beautiful solution for this case:

  • get the input vector (12345),
  • repeat it indefinitely (12345123451...),
  • take as many elements as the vector size plus one (123451),
  • create a vector of pairs by starting with a window of the first two elements and sliding it to the end (12, 23..., 51), and, finally,
  • apply a transformation to each pair.

I have used Eric Niebler's range-v3 library, because std::views::cycle hasn't made it yet into C 23 ranges.

[Demo]

#include <fmt/ranges.h>
#include <functional>  // multiplies, plus
#include <range/v3/all.hpp>
#include <vector>

template <typename C, typename F>
auto my_adjacent_transform(const C& c, F&& f) {
    return c
        | ranges::views::cycle
        | ranges::views::take(c.size()   1)
        | ranges::views::sliding(2)
        | ranges::views::transform([&f](auto&& p) {
              return std::forward<F>(f)(p[0], p[1]);
          });
}

int main() {
    std::vector<int> v{ 1, 2, 3, 4, 5 };
    fmt::print("v: {}\n", v);
    fmt::print("Adding pairs: {}\n", my_adjacent_transform(v, std::plus<>{}));
    auto w{ my_adjacent_transform(v, std::multiplies<>{})
        | ranges::to<std::vector<int>>()
    };
    fmt::print("Multiplying pairs: {}\n", w);
}

// Outputs:
//
//   v: [1, 2, 3, 4, 5]
//   Adding pairs: [3, 5, 7, 9, 6]
//   Multiplying pairs: [2, 6, 12, 20, 5]

CodePudding user response:

Just do it:

A.push_back(A[0]); // copy first element to end
for (int j = 0; j < A.size() - 1;   j)
    f(A[j], A[j 1]);
  • Related