Skip to main content

Section 13.8 Recipes for vectors

There are many facets to learning โ€œhow to programโ€. You need to learn the syntax of one or more languages; you need to develop a mental model for how algorithms operate; you must learn how computers represent data; and you must develop your ability to solve problems using all of these skills and knowledge. When you start learning to program, the low-level details like syntax occupy much of your attention. As you progress, you spend less and less time learning new syntax and more time learning to solve problems.
Problem solving is almost by definition a creative process. If we know how to solve the problem already, we would likely not consider it to be a โ€œproblemโ€. Instead it would be a task to get done. Every time you go to solve a problem, it will likely involve doing something new. But few problems are truly unique. For any new problem you are solving, chances are there is a similar problem which already has a solution that you can adapt to meet the new challenge. So an important part of learning to program effectively is developing a โ€œplaybookโ€ - a set of common problems and algorithms that can solve them.
In this section we will look at some common problems that can be solved using vectors. We will look at the algorithms used to solve them and how to implement them in C++.

Subsection 13.8.1 Finding things

Our first common pattern is a search, which involves traversing an vector while โ€œsearchingโ€ for a particular element.
Listing 13.8.1.
We start by assuming the value we are looking for is not present. This is done on line 10 where we set the location to the size of the array. If the size is 4, the only valid values are 0-3, so we know size() represents an impossible value. Then, we traverse the vector to check each element. If we find the target value in the vector, we change the location and break out of the loop. When the loop is done, if location still matches the size(), we know the value was not found.

Subsection 13.8.2 Reduce

Another common traversal is a reduce operation, which โ€œreducesโ€ an vector of values down to a single value. Examples include the sum or product of the elements, the minimum, and the maximum. The following function takes an vector and returns the sum of its elements. A reduction always needs an accumulator variable where we store the answer. As we visit each element, we update that variable as appropriate.
We have already seen one reduction recipe - calculating the sum of the values in a vector. In this recipe, our accumulator is the total and starts with a value of 0:
double sum(const vector<double>& values) {
    double total = 0.0;
    for (double value : values) {
        total += value;
    }
    return total;
}
Finding the smallest value in a vector can also be thought of as a reduction. In this case, our accumulator needs to keep track of the โ€œsmallest value we have seen so farโ€ (called minimum in the sample below). As we visit each element, we compare it to minimum, and if it is lower, we need to change minimum to store the new lowest value that we have just discovered. But for this to work, we canโ€™t start with 0 in our accumulator. If we did, we could go through the entire vector and never find anything smaller, and thus end up thinking that 0 was the minimum value. For situations like this it is common to use the first value to initialize the accumulator and then loop through all the elements after the first:
double minValue(const vector<double>& values) {
    // assume the first element is the minimum
    int minimum = values.at(0);
    // loop through the rest of the vector to see if any elements are smaller
    for (size_t i = 1; i < values.size(); ++i) {
        int value = values.at(i);
        if (value < minimum) {
            minimum = value; // found a new minimum
        }
    }
    return minimum;
}

Subsection 13.8.3 Map

A map operation transforms each element in a vector to a new value. In Sectionย 13.7 we saw two versions of the doubleValues function that performed a map operation. The first version created a new vector and returned it. The second version modified the original vector in place.

Subsection 13.8.4 Filter

Finally, a filter operation traverses a vector and only keeps values that meet a certain condition. Although we could do this by removing unwanted values from the vector we start with, it is much easier to build up a vector with just the ones we want. This final sample demonstrates a function that filters out 0 values from a vector, producing a new one with only non-zero values:
Listing 13.8.2.

Note 13.8.1.

Simple does not always mean โ€œbadโ€ or โ€œinefficientโ€. It will almost certainly be faster to build a new vector with just the desired values instead of removing multiple items from the existing vector.

Checkpoint 13.8.1.

Suppose vector<string> album has already been defined and is storing some number of strings representing song titles. Construct a block of code that counts how many songs have an e in them.

Checkpoint 13.8.2.

Build a function onlyPassing that takes a vector<char> that contains a list of characters representing letter grades (A, B, C, D, F) and returns a new vector that has all the grades but the Fs.
You have attempted of activities on this page.