How to find a specific element of a value in a vector?

Jun 8, 2021 at 12:38am
If I have 2 values being added multiple times to a vector like this :
1
2
	int one = 1, four = 4;
	std::vector<int> numbers{ one,one, one, four,four , one, four};

see how I have three "four" values added into the vector , how could I find the second to last four inside the vector? I'd assume I could find it by finding it's index like
 
numbers[four].at(4)

but that does not work.
Last edited on Jun 8, 2021 at 12:39am
Jun 8, 2021 at 1:19am
.at is the same as [] except it has an extra safety check that makes it slower. If for some weird reason (there are designs like a hash function where it could make sense) you don't know that your index is safe to use, .at is your answer for that.

.find can be called repeatedly, using the last found location as a new starting point to look for the next one until you find the one you want.
or you can just do it yourself in a for loop. the find logic isnt any cleaner for this task.
Find would work better here in reverse... there is a reversed find (rfind I think).
Jun 8, 2021 at 1:31am
There is probably some <algorithm> you can leverage, but let's just do the cheap and dirty way with a good old for loop:

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
50
51
52
53
54
55
56
57
58
59
60
61
62
// Example program
#include <iostream>
#include <vector>
using std::cout;

// returns index of the second-to-last instance of the given number_to_find
// returns -1 if there are not at least two instances of the number_to_find
int index_of_second_to_last(const std::vector<int>& numbers, int number_to_find)
{
    int count = 0;

    for (int i = (int)numbers.size() - 1; i >= 0; i--)
    {
        if (numbers[i] == number_to_find)
        {
            count++;
            if (count == 2)
            {
                return i;   
            }
        }
    }
    
    return -1;
}

void test_equals(int actual_value, int expected_value)
{
    if (actual_value == expected_value)
    {
        cout << "Passed\n";
    }
    else
    {
        cout << "Failed\n";   
    }
}

int main()
{
    // Tests
    {
	    std::vector<int> numbers { 1, 1, 1, 4, 4, 1, 4 };	
	    test_equals(index_of_second_to_last(numbers, 4), 4);
    }
    {
	    std::vector<int> numbers { };	
	    test_equals(index_of_second_to_last(numbers, 4), -1);
    }
    {
	    std::vector<int> numbers { 42 };	
	    test_equals(index_of_second_to_last(numbers, 42), -1);
    }
    {
	    std::vector<int> numbers { 1, 2, 3, 4 };	
	    test_equals(index_of_second_to_last(numbers, 4), -1);
    }
    {
	    std::vector<int> numbers { 4, 4 };	
	    test_equals(index_of_second_to_last(numbers, 4), 0);
    }
}


Edit: +1 to jonnin's idea of a 2x std::find with reverse iterators. Although, honestly, using a for loop might just be easier.
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// Example program
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
using std::cout;

// returns index of the second-to-last instance of the given number_to_find
// returns -1 if there are not at least two instances of the number_to_find
int index_of_second_to_last(const std::vector<int>& numbers, int number_to_find)
{
    auto first_it = std::find(numbers.rbegin(), numbers.rend(), number_to_find);
    auto second_it = std::find(std::next(first_it), numbers.rend(), number_to_find); // WARNING: bug. See later post in thread.
    
    // this is a reverse iterator, so it's the distance from the last element
    int distance = second_it - numbers.rbegin();
    if (distance == 0)
    {
        return -1;   
    }
    else
    {
        return (int)numbers.size() - distance - 1;
    }
}

void test_equals(int actual_value, int expected_value)
{
    if (actual_value == expected_value)
    {
        cout << "Passed\n";
    }
    else
    {
        cout << "Failed\n";   
    }
}

int main()
{
    // Tests
    {
	    std::vector<int> numbers { 1, 1, 1, 4, 4, 1, 4 };	
	    test_equals(index_of_second_to_last(numbers, 4), 4);
    }
    {
	    std::vector<int> numbers { 4, 4, 1, 1, 0, 0, 1, 0 };	
	    test_equals(index_of_second_to_last(numbers, 4), 0);
    }
    {
	    std::vector<int> numbers { 1, 1, 1, 4, 4, 1, 4, 1 };	
	    test_equals(index_of_second_to_last(numbers, 4), 4);
    }
    {
	    std::vector<int> numbers { };	
	    test_equals(index_of_second_to_last(numbers, 4), -1);
    }
    {
	    std::vector<int> numbers { 42 };	
	    test_equals(index_of_second_to_last(numbers, 42), -1);
    }
    {
	    std::vector<int> numbers { 1, 2, 3, 4 };	
	    test_equals(index_of_second_to_last(numbers, 4), -1);
    }
    {
	    std::vector<int> numbers { 4, 4 };	
	    test_equals(index_of_second_to_last(numbers, 4), 0);
    }
}
Last edited on Jun 8, 2021 at 2:26pm
Jun 8, 2021 at 9:16am
There seems to be an issue with test 4. I get a run-time exception with VS2019.

This works for all the tests with VS2019:

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
50
51
52
53
54
55
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
using std::cout;

// returns index of the second-to-last instance of the given number_to_find
// returns -1 if there are not at least two instances of the number_to_find
template <typename T>
int index_of_second_to_last(const std::vector<T>& numbers, T number_to_find)
{
	if (const auto first_it {std::find(numbers.crbegin(), numbers.crend(), number_to_find)}; first_it != numbers.crend())
		if (const auto second_it {std::find(std::next(first_it), numbers.crend(), number_to_find)}; second_it != numbers.crend())
			return static_cast<int>(std::distance(numbers.cbegin(), second_it.base()) - 1);

	return -1;
}

void test_equals(int actual_value, int expected_value)
{
	cout << (actual_value == expected_value ? "Passed" : "Failed") << '\n';
}

int main()
{
	// Tests
	{
		const std::vector numbers {1, 1, 1, 4, 4, 1, 4};
		test_equals(index_of_second_to_last(numbers, 4), 4);
	}
	{
		const std::vector numbers {4, 4, 1, 1, 0, 0, 1, 0};
		test_equals(index_of_second_to_last(numbers, 4), 0);
	}
	{
		const std::vector numbers {1, 1, 1, 4, 4, 1, 4, 1};
		test_equals(index_of_second_to_last(numbers, 4), 4);
	}
	{
		const std::vector<int> numbers { };
		test_equals(index_of_second_to_last(numbers, 4), -1);
	}
	{
		const std::vector numbers {42};
		test_equals(index_of_second_to_last(numbers, 42), -1);
	}
	{
		const std::vector numbers {1, 2, 3, 4};
		test_equals(index_of_second_to_last(numbers, 4), -1);
	}
	{
		const std::vector numbers {4, 4};
		test_equals(index_of_second_to_last(numbers, 4), 0);
	}
}



Passed
Passed
Passed
Passed
Passed
Passed
Passed

Last edited on Jun 8, 2021 at 9:18am
Jun 8, 2021 at 2:25pm
Thanks, it was because I was calling std::next on the rend iterator.
Quick fix for my second code is:
1
2
3
4
5
    auto first_it = std::find(numbers.rbegin(), numbers.rend(), number_to_find);
    if (first_it == numbers.rend())
    {
        return -1;
    }
Last edited on Jun 8, 2021 at 2:27pm
Jun 8, 2021 at 4:13pm
Thanks guys.
Jun 8, 2021 at 4:44pm
You could use find_if with a stateful predicate:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <algorithm>
#include <iterator>
#include <iostream>

int main()
{
  int xs[] = { 1, 2, 7, 8, 123, 4, 4, 8, 5, 4, 2, 10 }; 
  int number_to_find = 4;
  int nth = 3;

  auto const iter = std::find_if(std::begin(xs), std::end(xs), 
    [=](auto x) mutable { return (number_to_find == x && --nth == 0); });
    
  // *iter is the second 4 
  std::cout << "occurrence number " << nth << " of the value " << number_to_find 
            << " is located at index " << std::distance(std::begin(xs), iter) 
            << " in the array\n";  
}
Jun 8, 2021 at 4:59pm
That solves a different problem than the original one, assuming the original problem is "finding the [nth] to last [number]".
Last edited on Jun 8, 2021 at 5:00pm
Jun 8, 2021 at 6:00pm
Okay, to fix it you have to use std::rbegin and std::rend instead.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <algorithm>
#include <iterator>
#include <iostream>

int main()
{
  int xs[] = { 1, 2, 7, 8, 123, 4, 4, 8, 5, 4, 2, 10 }; 
  int number_to_find = 4;
  int nth = 2;

  auto const iter = std::find_if(std::rbegin(xs), std::rend(xs), 
    [=](auto x) mutable { return (number_to_find == x && --nth == 0); });
    
  std::cout << "The nth-to-last occurence of the value " << number_to_find 
            << " (where n = " << nth << ") is located at index " 
            << std::size(xs) - std::distance(std::rbegin(xs), iter) - 1
            << " in the array\n";  
}
Last edited on Jun 8, 2021 at 6:02pm
Topic archived. No new replies allowed.