for_each
adjacent elements
coding patterns
algorithm design
programming techniques

for_each that gives two or n adjacent elements

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

The standard std::for_each operates on one element at a time, but many algorithms need to process pairs or windows of adjacent elements — computing differences, detecting changes, or applying sliding-window operations. C++23 added std::views::adjacent and std::views::slide for this purpose. In C++17 and earlier, use std::adjacent_find with a custom predicate, manual iterator arithmetic, or a custom for_each_adjacent function. For Python and JavaScript, the equivalent patterns use zip, itertools.pairwise, or Array.reduce.

C++23: views::adjacent and views::slide

cpp
1#include <ranges>
2#include <vector>
3#include <iostream>
4
5int main() {
6    std::vector<int> v = {1, 4, 2, 8, 5, 7};
7
8    // adjacent<2> gives pairs of consecutive elements
9    for (auto [a, b] : v | std::views::adjacent<2>) {
10        std::cout << a << ", " << b << " -> diff: " << b - a << "\n";
11    }
12    // 1, 4 -> diff: 3
13    // 4, 2 -> diff: -2
14    // 2, 8 -> diff: 6
15    // 8, 5 -> diff: -3
16    // 5, 7 -> diff: 2
17
18    // adjacent<3> gives triples
19    for (auto [a, b, c] : v | std::views::adjacent<3>) {
20        std::cout << a << ", " << b << ", " << c << "\n";
21    }
22    // 1, 4, 2
23    // 4, 2, 8
24    // 2, 8, 5
25    // 8, 5, 7
26
27    return 0;
28}

std::views::adjacent<N> produces a view of tuples containing N consecutive elements from the range. It is a compile-time fixed-size sliding window.

C++23: views::slide (Runtime Window Size)

cpp
1#include <ranges>
2#include <vector>
3#include <iostream>
4
5int main() {
6    std::vector<int> v = {1, 2, 3, 4, 5};
7
8    // slide(3) gives windows of size 3 as subranges
9    for (auto window : v | std::views::slide(3)) {
10        for (int x : window) {
11            std::cout << x << " ";
12        }
13        std::cout << "\n";
14    }
15    // 1 2 3
16    // 2 3 4
17    // 3 4 5
18
19    return 0;
20}

std::views::slide(n) produces subranges of size n, where n can be determined at runtime. Each subrange is a view into the original container.

C++17: Custom for_each_adjacent

cpp
1#include <vector>
2#include <iostream>
3
4template <typename Iter, typename Func>
5void for_each_adjacent(Iter begin, Iter end, Func func) {
6    if (begin == end) return;
7    auto prev = begin;
8    for (auto it = std::next(begin); it != end; ++it) {
9        func(*prev, *it);
10        prev = it;
11    }
12}
13
14int main() {
15    std::vector<int> v = {10, 20, 15, 30, 25};
16
17    for_each_adjacent(v.begin(), v.end(), [](int a, int b) {
18        std::cout << a << " -> " << b;
19        if (b > a) std::cout << " (increase)";
20        else std::cout << " (decrease)";
21        std::cout << "\n";
22    });
23    // 10 -> 20 (increase)
24    // 20 -> 15 (decrease)
25    // 15 -> 30 (increase)
26    // 30 -> 25 (decrease)
27
28    return 0;
29}

This is the pre-C++23 solution. The template works with any forward iterator and any binary function.

C++17: N-Element Sliding Window

cpp
1#include <vector>
2#include <iostream>
3#include <numeric>
4
5template <typename Iter, typename Func>
6void for_each_window(Iter begin, Iter end, int window_size, Func func) {
7    if (std::distance(begin, end) < window_size) return;
8    for (auto it = begin; it + window_size <= end; ++it) {
9        func(it, it + window_size);
10    }
11}
12
13int main() {
14    std::vector<int> v = {1, 3, 5, 7, 9, 11};
15
16    // Moving average with window size 3
17    for_each_window(v.begin(), v.end(), 3,
18        [](auto begin, auto end) {
19            double sum = std::accumulate(begin, end, 0.0);
20            double avg = sum / std::distance(begin, end);
21            std::cout << "avg: " << avg << "\n";
22        });
23    // avg: 3
24    // avg: 5
25    // avg: 7
26    // avg: 9
27
28    return 0;
29}

Using std::adjacent_find as a Workaround

cpp
1#include <algorithm>
2#include <vector>
3#include <iostream>
4
5int main() {
6    std::vector<int> v = {1, 3, 2, 5, 4};
7
8    // adjacent_find visits pairs — abuse it to process all pairs
9    // by always returning false
10    std::adjacent_find(v.begin(), v.end(),
11        [](int a, int b) {
12            std::cout << "pair: " << a << ", " << b << "\n";
13            return false;  // Never "find" — visits all pairs
14        });
15    // pair: 1, 3
16    // pair: 3, 2
17    // pair: 2, 5
18    // pair: 5, 4
19
20    return 0;
21}

This is a hack — std::adjacent_find is designed to find the first adjacent pair matching a predicate, but returning false forces it to visit every pair. Use the custom function template for production code.

Python: itertools.pairwise (3.10+)

python
1from itertools import pairwise
2
3data = [10, 20, 15, 30, 25]
4
5# pairwise gives consecutive pairs
6for a, b in pairwise(data):
7    diff = b - a
8    print(f"{a} -> {b}: {'up' if diff > 0 else 'down'} {abs(diff)}")
9# 10 -> 20: up 10
10# 20 -> 15: down 5
11# 15 -> 30: up 15
12# 30 -> 25: down 5
13
14# N-element sliding window
15from itertools import islice
16
17def sliding_window(iterable, n):
18    it = iter(iterable)
19    window = list(islice(it, n))
20    if len(window) == n:
21        yield tuple(window)
22    for x in it:
23        window = window[1:] + [x]
24        yield tuple(window)
25
26for window in sliding_window(data, 3):
27    print(window)
28# (10, 20, 15)
29# (20, 15, 30)
30# (15, 30, 25)

JavaScript: Adjacent Element Patterns

javascript
1const data = [10, 20, 15, 30, 25];
2
3// Process adjacent pairs
4data.forEach((val, i) => {
5  if (i === 0) return;
6  const prev = data[i - 1];
7  console.log(`${prev} -> ${val}: diff ${val - prev}`);
8});
9
10// Sliding window of size n using reduce
11function slidingWindow(arr, n) {
12  return arr.slice(0, arr.length - n + 1).map((_, i) => arr.slice(i, i + n));
13}
14
15console.log(slidingWindow(data, 3));
16// [[10,20,15], [20,15,30], [15,30,25]]
17
18// Using Array.reduce for pairwise
19const diffs = data.reduce((acc, val, i) => {
20  if (i > 0) acc.push(val - data[i - 1]);
21  return acc;
22}, []);
23console.log(diffs);  // [10, -5, 15, -5]

Common Pitfalls

  • Off-by-one with window size: A container of N elements has N-1 adjacent pairs, N-2 triples, and so on. Forgetting this leads to out-of-bounds access. Always check that the container size is at least the window size before iterating.
  • Iterator invalidation: If the callback modifies the container (inserting or erasing elements), iterators become invalid. Never modify the container during a sliding-window traversal. Copy data if mutation is needed.
  • Using std::adjacent_find for side effects: The standard does not guarantee adjacent_find processes all pairs if it finds a match. The return false hack works for visiting all pairs but is fragile and unclear. Use a custom function instead.
  • Performance with large windows: A naive sliding window copies elements for each position. For large windows, use a deque-based approach that adds one element and removes one per step, achieving O(1) per window instead of O(window_size).
  • views::adjacent requires random access: In C++23, std::views::adjacent<N> works best with random-access ranges. For forward-only ranges (like std::forward_list), it may not compile or may have degraded performance. Check the range concept requirements.

Summary

  • C++23 provides std::views::adjacent<N> (compile-time) and std::views::slide(n) (runtime) for sliding windows
  • In C++17, write a custom for_each_adjacent template using iterator pairs
  • std::adjacent_find can visit all pairs as a workaround but is not designed for this purpose
  • Python 3.10+ has itertools.pairwise() for adjacent pairs; use a generator for arbitrary window sizes
  • JavaScript uses index-based iteration or Array.slice for sliding windows
  • Always check that the container has enough elements for the requested window size

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.