Help with vectors

Oct 12, 2019 at 5:38pm
So today, I decided to learn vectors. Nothing much. Learned how to read data from file to a vector and all basic vectors functions. So then, I decided to practice all of it. And what I wanted to do first is to remove some elements that are for example greater than 15. Now with an array, it has been easier (probably because I knew how to do it). I would just make a second for loop. However, I don't think it's possible with vectors. But they have .erase function. So I tried it in for loop. But that doesn't work as it supposed to.

1
2
3
4
5
6
7
8
9
10
11
12
//---
void removeElement(vector<int>& Skaiciai)
{
  for(unsigned int i = 0; i < Skaiciai.size(); i++)
  {
    if(Skaiciai[i] < 15)
    {
      Skaiciai.erase(Skaiciai.begin() + i);
    }
  }
}
//--- 
Oct 12, 2019 at 5:47pm
The only sense that it's "easier" with an array is that you can't physically do it in the first place; an array has a fixed size.

Using erase like that is error-prone because let's say you're currently at i = 5, and you erase element Skaiciai[5]... so what's now at Skaiciai[5] after the erase? Well, Skaiciai[6] (or perhaps nothing, if you were already at the end of the array).

So in a sense, your current index becomes invalidated when you erase something.

To avoid this, the best thing would to avoid writing your own loop in the first place. Use the standard library to help you.
See: https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Example program
#include <iostream>
#include <vector>
#include <algorithm>

using std::vector;

bool isLessThan15(int n) { return n < 15; }

void removeLessThan15(vector<int>& vec)
{
    // Removes all numbers less than 15.
    vec.erase(std::remove_if(vec.begin(), vec.end(), isLessThan15), vec.end());
}

void print(const vector<int>& vec)
{
    for (const int i : vec)
    {
        std::cout << i << ' ';   
    }
    std::cout << '\n';
}

int main()
{
    vector<int> vec = { 16, 3, 14, 15, 17 };
    removeLessThan15(vec);
    print(vec);
}
Last edited on Oct 12, 2019 at 5:50pm
Oct 12, 2019 at 6:03pm
Okay, but let's say that we don't know what to remove yet. Let's say we need to find first the sum of the elements and then it's average. And then remove every element that is lower than average. Since I'm new I don't know how would I change this

 
bool isLessThan15(int n) { return n < 15; }


Would I just pass the function that calculated the average to this boolean ?
Oct 12, 2019 at 6:23pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>

int main()
{
    std::vector<int> seq { 12, 24, 58, 67, 92, 45, 77, 22, 56, 89, 21, 80 } ;
    for( int v : seq ) std::cout << v << ' ' ;
    std::cout << '\n' ;

    // find the sum of all the elements
    // https://en.cppreference.com/w/cpp/algorithm/accumulate
    const double sum = std::accumulate( seq.begin(), seq.end(), 0.0 ) ;

    const double average = sum / seq.size() ; // find the average
    std::cout << "average: " << average << '\n' ;

    // remove all elements less than average
    // https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom
    const auto less_than_avg = [average] ( int v ) { return v < average ; } ;
    seq.erase( std::remove_if( seq.begin(), seq.end(), less_than_avg ), seq.end() ) ;

    for( int v : seq ) std::cout << v << ' ' ;
    std::cout << '\n' ;
}

http://coliru.stacked-crooked.com/a/339d6bfc976a77c6
Oct 12, 2019 at 6:28pm
we need to find first the sum of the elements and then it's average. And then remove every element that is lower than average.

Well, there's many ways to skin this cat. I would still prefer to use remove_if. If I choose to go with the same pattern as before, I could do something like the following. Note that you wouldn't re-calculate the average each time, just once (unless you want it to be a rolling-average or something like that).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// Example program
#include <iostream>
#include <vector>
#include <algorithm>

using std::vector;

struct LessThanAverage {
    LessThanAverage(double average)
    {
        this->average = average;   
    }
    bool operator()(double n)
    {
        return n < average;   
    }
  private:
    double average;
};

double average(const vector<int>& vec)
{
    double sum = 0.0;
    for (int i : vec)
        sum += i;
    return (vec.size() == 0) ? 0.0 : sum / vec.size();
}

void removeLessThanAverage(vector<int>& vec)
{
    double avg = average(vec);
    vec.erase(std::remove_if(vec.begin(), vec.end(), LessThanAverage(avg)), vec.end());
}

void print(const vector<int>& vec)
{
    for (const int i : vec)
    {
        std::cout << i << ' ';   
    }
    std::cout << '\n';
}

int main()
{
    vector<int> vec = { 16, 3, 14, 15, 4 };
    removeLessThanAverage(vec);
    print(vec);
}


remove_if's UnaryPredicate can use a classes's operator() returning a bool as the predicate.
https://en.cppreference.com/w/cpp/algorithm/remove_if

Edit: Of course, JLBorges' example, which uses a lambda to capture the average, is much more concise than mine. (I need to get more comfortable with using those...!)
Last edited on Oct 12, 2019 at 6:35pm
Oct 12, 2019 at 7:00pm
Every time I'm trying to do the line

 
const auto lessThen = [average] (int v) {return v < average;};


It just gives me an error

'lessThen' was not declared in this scope"
'lessThen' does not name a type"
Oct 12, 2019 at 7:03pm
Does this compile for you?
1
2
3
4
5
6
7
#include <iostream>

int main()
{
    int arr[3] = {3, 2, 1};
    for (auto i : arr) std::cout << i;
}

What is your compiler version?
Last edited on Oct 12, 2019 at 7:04pm
Oct 12, 2019 at 7:10pm
No. this not compile for me. I'm using Code::Blocks 17.12
Oct 12, 2019 at 7:21pm
Go to Settings --> Compiler --> Compiler Flags tab, and check "Have g++ follow the C++14 ISO C++ language standard [-std=c++14]". (If -std=c++17 is available, you can also try that. If nothing else, -std=c++11 should be available)

My previous post will compile with -std=c++11 or higher.
Last edited on Oct 12, 2019 at 7:30pm
Oct 12, 2019 at 7:26pm
In the long term, I suggest updating your compiler.
CodeBlocks download page wrote:
The codeblocks-17.12mingw-setup.exe file includes additionally the GCC/G++ compiler and GDB debugger from TDM-GCC (version 5.1.0, 32 bit, SJLJ).


Plugging an updated compiler to CodeBlocks or another IDE is a good skill to learn.

You can download an even newer MinGW compiler from sites like here: https://nuwen.net/mingw.html
And I think these directions tell you how to point CodeBlocks to it: http://wiki.codeblocks.org/index.php/MinGW_installation
Last edited on Oct 12, 2019 at 7:29pm
Oct 12, 2019 at 7:34pm
Thank you, it worked ! But now I just have questions (so I learn it rather than memorize it)

1
2
const auto less_than_avg = [average] ( int v ) { return v < average ; } ;
seq.erase( std::remove_if( seq.begin(), seq.end(), less_than_avg ), seq.end() ) ;


What does the const auto mean ?

Next thing. Does the less_than_avg get assigned to whatever value is in [average] ? And then there is a new int v, right ? And you use the v to check whether the condition is true or false right ? (just like previously you showed it with the boolean if we know what we want to remove already)

And the .erase function. Why after less_than_avg there's again seq.end() ?

 
seq.erase( std::remove_if( seq.begin(), seq.end(), less_than_avg ), seq.end() ) ;
Oct 12, 2019 at 8:39pm
const means constant, a value that cannot be assigned a new value:
const double pi = 3.14; //ok
pi = 2.68; //error, its a constant!

and auto means 'let the compiler determine the type'.

less than average returns the result of a Boolean expression: its true or false.

int v is just a parameter. its a function, just a little different from what you have seen before, called a lambda which are used mostly for one-line functions that are fed into standard algorithms like sort (how to compare values) or the like.

the erase is using less than average to figure out what to erase. Its saying to erase when that is TRUE.

so for everything in the container, erase it if its less than average.

This is advanced, modern style c++ where the language does most of the work (iteration/looping/logic) and you just guide it a bit.


Topic archived. No new replies allowed.