C++ - Vectors & Lists (filter, trim)

Overview

Estimated time: 50–70 minutes

Master the everyday containers: std::vector (dynamic array) and std::list (linked list). Practice multiple filtering and trimming techniques using both classic algorithms and modern ranges.

Learning Objectives

  • Create, modify, and iterate std::vector and std::list.
  • Filter elements using erase-remove, std::erase_if (C++20), and ranges::views::filter (C++20).
  • Trim containers by predicate (remove leading/trailing elements) and trim strings (whitespace) within containers.

Prerequisites

std::vector basics (use by default)

#include <iostream>
#include <vector>

int main(){
  std::vector v = {10, 20, 30};
  v.push_back(40);
  for (int x : v) std::cout << x << " ";
  std::cout << "\n";
}

Expected Output: 10 20 30 40

Filter: erase-remove idiom (pre-C++20 and portable)

#include <vector>
#include <algorithm>
#include <iostream>

int main(){
  std::vector v = {1,2,3,4,5,6};
  // Remove even numbers
  v.erase(std::remove_if(v.begin(), v.end(), [](int x){ return x % 2 == 0; }), v.end());
  for (int x : v) std::cout << x << ' ';
  std::cout << "\n";
}

Expected Output: 1 3 5

Filter: std::erase_if (C++20)

#include <vector>
#include <algorithm> // std::erase_if
#include <iostream>

int main(){
  std::vector names = {"", " Ann ", "Ben", "  ", "Cat"};
  std::erase_if(names, [](const std::string& s){ return s.empty(); });
  for (auto& s : names) std::cout << '[' << s << "]\n";
}

Expected Output: [] [ Ann ] [Ben] [ ] [Cat] (empty entries removed)

Trim strings inside a vector

Implement a simple trim helper and apply it to each string. Then remove empty results.

#include <string>
#include <vector>
#include <algorithm>
#include <cctype>

static inline void trim_inplace(std::string& s){
  auto not_space = [](unsigned char ch){ return !std::isspace(ch); };
  // left trim
  s.erase(s.begin(), std::find_if(s.begin(), s.end(), not_space));
  // right trim
  s.erase(std::find_if(s.rbegin(), s.rend(), not_space).base(), s.end());
}

int main(){
  std::vector items = {"  apple ", " ", " banana", "carrot  "};
  for (auto& s : items) trim_inplace(s);
  // Drop empties
  items.erase(std::remove_if(items.begin(), items.end(), [](const std::string& s){ return s.empty(); }), items.end());
  for (auto& s : items) std::cout << s << "\n";
}

Expected Output: apple banana carrot

Trim container by predicate (leading/trailing)

Example: trim leading/trailing zeros in a vector.

#include <vector>
#include <algorithm>
#include <iostream>

template <class T, class Pred>
void trim_container(std::vector& v, Pred is_trim){
  // left trim
  auto first = std::find_if_not(v.begin(), v.end(), is_trim);
  // right trim
  auto last = std::find_if_not(v.rbegin(), v.rend(), is_trim).base();
  if (first < last) {
    v.assign(first, last);
  } else {
    v.clear();
  }
}

int main(){
  std::vector v = {0,0,1,2,3,0,0};
  trim_container(v, [](int x){ return x==0; });
  for (int x : v) std::cout << x << ' ';
}

Expected Output: 1 2 3

Ranges view filter (C++20)

#include <vector>
#include <ranges>
#include <iostream>

int main(){
  std::vector v = {1,2,3,4,5,6};
  auto evens = v | std::views::filter([](int x){ return x%2==0; });
  // Copy into a new vector if needed
  std::vector out;
  for (int x : evens) out.push_back(x);
  for (int x : out) std::cout << x << ' ';
}

Expected Output: 2 4 6

std::list basics (when you need stable iterators, frequent splicing)

#include <list>
#include <iostream>

int main(){
  std::list lst = {1,2,3};
  lst.push_front(0);
  for (int x : lst) std::cout << x << ' ';
}

Expected Output: 0 1 2 3

list filtering and trimming

#include <list>
#include <algorithm>
#include <iostream>

int main(){
  std::list lst = {0,0,10,20,0,30,0};
  // list::remove_if is member function
  lst.remove_if([](int x){ return x==0; });
  for (int x : lst) std::cout << x << ' ';
  std::cout << "\n";

  // Trim leading/trailing values by predicate
  std::list lst2 = {0,0,1,2,3,0,0};
  auto is_zero = [](int x){ return x==0; };
  while (!lst2.empty() && is_zero(lst2.front())) lst2.pop_front();
  while (!lst2.empty() && is_zero(lst2.back())) lst2.pop_back();
  for (int x : lst2) std::cout << x << ' ';
}

Expected Output: 10 20 30 1 2 3

Beginner Boosters

#include 
#include 
#include 
int main(){
  std::vector v{5,4,3,2,1};
  // Print with indices
  for (std::size_t i=0;i=3 (erase-remove)
  v.erase(std::remove_if(v.begin(), v.end(), [](int x){return x<3;}), v.end());
  for (int x : v) std::cout << x << ' ';
}

Expected Output (example): 0:5 1:4 2:3 3:2 4:1 5 4 3

#include 
#include 
int main(){
  std::list names = {" Ann ", " Bob", "", "Cat "};
  // Drop empties
  names.remove_if([](const std::string& s){ return s.empty(); });
  for (auto& s : names) std::cout << '[' << s << "]\n";
}

Expected Output: [ Ann ] [ Bob] [Cat ]

Common Pitfalls

  • std::vector erase invalidates iterators and indices after the erased point. Recompute iterators if you continue iterating.
  • For filtering, remember the erase-remove idiom; std::remove_if doesn’t shrink the container by itself.
  • Prefer std::vector by default for cache locality and overall performance; std::list is for special cases (stable iterators, frequent splicing).

Checks for Understanding

  1. When do you prefer std::vector over std::list?
  2. What does the erase-remove idiom achieve?
  3. Show one way to trim leading/trailing elements by predicate.
Show answers
  1. Almost always; vector is contiguous and fast. Use list for stable iterators or splicing.
  2. It removes elements matching a predicate and then erases the "moved-to-end" range to shrink the vector.
  3. Find first/last non-matching iterators and assign the subrange; or pop_front/pop_back while predicate holds.

Exercises

  1. Write a function filter_even(std::vector<int>) that returns a new vector with only even numbers (use ranges if available, else erase-remove).
  2. Implement trim_by_pred(std::vector<T>&, Pred) that trims both ends by predicate (as shown).
  3. Given std::vector<std::string>, trim whitespace on each string and remove empties. Compare pre-C++20 and C++20 solutions.