Erasing duplicates from two vectors using only iterators - c++

How can I delete duplicates from two vectors of strings (delete them from both vectors) using only iterators?
I suppose it doesn't work because if values are already deleted they can't be compared, but I can not think of any other solution, only if I had one function to erase both elements at the same time.
void obrisiIsteRijeci(std::vector<std::string>& v1, std::vector<std::string>& v2){
for(auto it = v1.begin(); it != v1.end(); it++){
auto it1 = it;
for(auto it2 = v2.begin(); it2 != v2.end(); it2++){
if((*(it2) == *(it1)) && (*(it1) == *(it2))){
v1.erase(it1);
v2.erase(it2);
}
}
}
}

I can suggest the following approach. In the demonstration program below I am using vectors of the type std::vector<int> for simplicity.
#include <iostream>
#include <vector>
#include <iterator>
$include <algorithm>
int main()
{
std::vector<int> v1 = { 1, 2, 1, 2, 3, 4 }, v2 = { 1, 2, 3, 5 };
for (auto first = std::begin( v1 ); first != std::end( v1 ); )
{
auto it = std::find( std::begin( v2 ), std::end( v2 ), *first );
if (it != std::end( v2 ))
{
v2.erase( std::remove( it, std::end( v2 ), *first ), std::end( v2 ) );
auto value = *first;
auto offset = std::distance( std::begin( v1 ), first );
v1.erase( std::remove( first, std::end( v1 ), value ), std::end( v1 ) );
first = std::next( std::begin( v1 ), offset );
}
else
{
++first;
}
}
for (const auto &item : v1)
{
std::cout << item << ' ';
}
std::cout << '\n';
for (const auto &item : v2)
{
std::cout << item << ' ';
}
std::cout << '\n';
}
The program output is
4
5

Related

copy 2d vector without first row and column

Just like in topic. I would like to copy one vector to another without first row and column.
'''
std::vector<std::vector<int>> v2(v1.size()-1,std::vector<int>(v1.size()-1));
std::copy((v1.begin()+1)->begin()+1,v1.end()->end(),v2.begin()->begin());
return v2;
'''
Using C++ and views it is easy to drop items while enumerating.
So you can avoid using raw or iterator loops.
Live demo here : https://godbolt.org/z/8xz91Y8cK
#include <ranges>
#include <iostream>
#include <vector>
auto reduce_copy(const std::vector<std::vector<int>> values)
{
std::vector<std::vector<int>> retval{};
// drop first row
for (const auto& row : values | std::views::drop(1))
{
// add a new row to retval
auto& new_row = retval.emplace_back();
// drop first column
for (const auto& col : row | std::views::drop(1))
{
new_row.emplace_back(col);
}
}
return retval;
}
int main()
{
std::vector<std::vector<int>> values{ {1,2,3}, {4,5,6}, {7,8,9} };
auto result = reduce_copy(values);
for (const auto& row : result)
{
for (const auto& value : row)
{
std::cout << value << " ";
}
std::cout << "\n";
}
return 0;
}
You can use for example the ordinary for loop
#include <vector>
#include <iterator>
#include <algorithm>
//...
for ( auto first = std::next( std::begin( v1 ) ), target = std::begin( v2 );
first != std::end( v1 );
++first, ++target )
{
std::copy( std::next( std::begin( *first ) ), std::end( *first ), std::begin( *target ) );
}

Delete the selected item in forward_list C++

I need to somehow remove an element in a list if it is already in another list. I created a function but it doesn't work. Tell me how to fix it. thank you very much.
void compare(forward_list<string> list_1, forward_list<string> list_2) {
auto prev = list_1.before_begin();
for (int i = 0; i < Size(list_1); i++) {
auto l_front = list_1.begin();
advance(l_front, i);
for (int j = 0; j < Size(list_2); j++) {
auto l_front_2 = list_2.begin();
advance(l_front_2, j);
if (*l_front == *l_front_2)
{
l_front_2 = list_2.erase_after(l_front);
}
}
}
}
It seems you are trying to remove elements from list_2 that are found in list_1. But in this statement
l_front_2 = list_2.erase_after(l_front);
you are using the iterator l_front from list_1 that does not make a sense.
Also if an element in list_2 is removed then due to the expression j++ in the for loop
for (int j = 0; j < Size(list_2); j++) {
the next element in the list will be skipped.
A straightforward approach can look for example the following way as it is shown in the demonstration program below.
#include <iostream>
#include <string>
#include <forward_list>
#include <iterator>
#include <algorithm>
void make_unique( std::forward_list<std::string> &list_1,
const std::forward_list<std::string> &list_2 )
{
for ( auto current = std::begin( list_1 ); current != std::end( list_1 ); )
{
if (std::find( std::begin( list_2 ), std::end( list_2 ), *current ) != std::end( list_2 ))
{
std::string s( *current );
list_1.remove( s );
current = std::begin( list_1 );
}
else
{
std::advance( current, 1 );
}
}
}
int main()
{
std::forward_list<std::string> list_1 = { "A", "B", "A", "C", "D", "B", "E" };
std::forward_list<std::string> list_2 = { "A", "B" };
for (const auto &s : list_1)
{
std::cout << s << ' ';
}
std::cout << '\n';
make_unique( list_1, list_2 );
for (const auto &s : list_1)
{
std::cout << s << ' ';
}
std::cout << '\n';
}
The program output is
A B A C D B E
C D E
A much more simple function definition can be if to use the method remove_if or the general function std::erase_if introduced in the C++ 20 Standard. For example
void make_unique( std::forward_list<std::string> &list_1,
const std::forward_list<std::string> &list_2 )
{
auto found = [&list_2]( const auto &s )
{
return std::find( std::begin( list_2 ), std::end( list_2 ), s ) != std::end( list_2 );
};
list_1.remove_if( found );
}
Or if the compiler supports C++ 20 then
void make_unique( std::forward_list<std::string> &list_1,
const std::forward_list<std::string> &list_2 )
{
auto found = [&list_2]( const auto &s )
{
return std::find( std::begin( list_2 ), std::end( list_2 ), s ) != std::end( list_2 );
};
std::erase_if( list_1, found );
}
The both presented functions remove elements in list_1 that are found in list_2.

Remove duplicate elements from std::vector but starting from the front?

How can I delete duplicate elements from a vector but starting from the front?
So
2 3 4 5 2 5 would become 3 4 2 5
1 5 3 1 would become 5 3 1
I would like the solution to be easy to read, and it would be nice if it had good performance as well.
If a container supports bidirectional iterators then it is unimportant from which side of the container you are trying to remove duplicate elements because you can use reverse iterators.
Here is a demonstrative program.
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
template <typename ForwardIterator>
ForwardIterator remove_duplicates( ForwardIterator first, ForwardIterator last )
{
for ( ; first != last; ++first )
{
last = std::remove( std::next( first ), last, *first );
}
return last;
}
int main()
{
std::vector<int> v = { 1, 2, 3, 4, 5, 4, 3, 2, 1 };
for ( const auto &item : v ) std::cout << item << ' ';
std::cout << '\n';
v.erase( remove_duplicates( std::begin( v ), std::end( v ) ), std::end( v ) );
for ( const auto &item : v ) std::cout << item << ' ';
std::cout << '\n';
std::cout << '\n';
v.assign( { 1, 2, 3, 4, 5, 4, 3, 2, 1 } );
for ( const auto &item : v ) std::cout << item << ' ';
std::cout << '\n';
v.erase( std::begin( v ), remove_duplicates( std::rbegin( v ), std::rend( v ) ).base() );
for ( const auto &item : v ) std::cout << item << ' ';
std::cout << '\n';
}
The program output is
1 2 3 4 5 4 3 2 1
1 2 3 4 5
1 2 3 4 5 4 3 2 1
5 4 3 2 1
An asymptotically efficient algorithm (N log N):
Create a range of iterators each pointing to the original container
Sort the iterators with a stable sort. Use a comparison function that indirects through the iterator.
Remove consecutive duplicates (std::unique) using reverse iterator and similar custom comparison function.
std::remove_if from the vector with a predicate function that removes elements whose iterator isn't in the secondary container.
It's a bit more complex than the O(N*N) solution. Here is an example implementation. I cannot guarantee that it is correct:
template<class It, class Sentinel>
auto remove_duplicates(It first, Sentinel last)
{
std::vector<It> iterators;
auto iterator_generator = [it = first]() mutable {
return it++;
};
std::generate_n(
std::back_inserter(iterators),
last - first,
iterator_generator);
auto iterator_compare = [](const auto& l, const auto& r) {
return *l < *r;
};
std::stable_sort(
iterators.begin(),
iterators.end(),
iterator_compare);
auto iterator_eq = [](const auto& l, const auto& r) {
return *l == *r;
};
auto last_unique = std::unique(
iterators.begin(),
iterators.end(),
iterator_eq);
iterators.erase(last_unique, iterators.end());
auto keep_generator = [it = first]() mutable {
return it++;
};
std::vector<bool> remove(last - first, true);
for(auto it : iterators) {
auto index = it - first;
remove[index] = false;
}
auto remove_predicate = [index = 0, remove = std::move(remove)](const auto& el) mutable {
return remove[index++];
};
return std::remove_if(first, last, std::move(remove_predicate));
}
// usage with reverse iterators
v.erase(
v.rend().base(),
remove_duplicates(v.rbegin(), v.rend()).base());

How do I get the first layer index of 2D vector?

I need help getting the first layer index of a 2D vector. Each element is unique, so there are no repetitions of element. Here's what I mean bellow.
I have a vector defined as:
vector<vector<int>> vec { {0,1} ,
{2} ,
{3,4},
{5,6} }
Then, I want to get the index of where any of the numbers is, on the "first" layer.
By this, I mean if I say
index of 4, it should return 2.
If I say, index of 6, it should return 3.
Thank you in advance!
You can use std::find and std::find_if:
#include <vector>
#include <algorithm>
#include <iostream>
int main()
{
std::vector<std::vector<int>> vec { {0,1} ,
{2} ,
{3,4},
{5,6} };
// Search for the 4
auto iter = std::find_if(vec.begin(), vec.end(), [](const std::vector<int>& v)
{return std::find(v.begin(), v.end(), 4) != v.end();});
// Output the distance between the item and the beginning of the vector
std::cout << std::distance(vec.begin(), iter);
}
Output:
2
The outer std::find_if searches the std::vector<vector<int>> and the argument to the lambda will be a reference to each inner vector. The inner std::find searches that inner vector for the value.
You could write a function that calculates the index like:
int findIndex(const std::vector<std::vector<int>> &vec, int val)
{
auto it = std::find_if(vec.cbegin(), vec.cend(), [val](const std::vector<int> &v) {
return std::find(v.cbegin(), v.cend(), val) != v.cend();
});
return it != vec.cend() ? std::distance(vec.cbegin(), it) : -1;
}
You can use the standard algorithm std::find_if along with the algorithm std::find.
Here is a demonstrative program.
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
int main()
{
std::vector<std::vector<int>> v =
{
{ 0, 1 }, { 2 }, { 3, 4 }, { 5, 6 }
};
auto present = []( const auto &v, const auto &value )
{
return std::find( std::begin( v ), std::end( v ), value ) != std::end( v );
};
int value = 4;
size_t i = std::distance( std::begin( v ),
std::find_if( std::begin( v ), std::end( v ),
[&, present, value]( const auto &item )
{
return present( item, value );
} ) );
if ( i != v.size() ) std::cout << value << ": " << i << '\n';
value = 6;
i = std::distance( std::begin( v ),
std::find_if( std::begin( v ), std::end( v ),
[&, present, value]( const auto &item )
{
return present( item, value );
} ) );
if ( i != v.size() ) std::cout << value << ": " << i << '\n';
return 0;
}
The program output is
4: 2
6: 3
You can use a hash-table data structure like unordered_map to do this in O(1) time.
unordered_map <int,int> m;
for(int i=0;i<vec.size();i++){
for(int j=0;j<vec[i].size();j++){
m[vec[i][j]] = i;
}
}

How to create a new vector with particular columns from existing 2D vector in c++

I have 2D Vector (vector<vector<string>>) with a lot of columns (m*n) (Here I mentioned this 2D Vector as Maintable). I want to create a new vector with a few particular columns from main table.
For Example, Suppose If I have a main table with 12 columns, I want to take any 3 Non Contiguous columns from the main table into new 2D Vector. How to do that?
You can use something as the following
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
//...
const size_t N = 10;
std::string a[] = { "A", "B", "C", "D", "E", "F" };
std::vector<std::vector<std::string>> v1( N, std::vector<std::string>( std::begin( a ), std::end( a ) ) );
std::vector<std::vector<std::string>> v2;
v2.reserve( v1.size() );
for ( const std::vector<std::string> &v : v1 )
{
v2.push_back( std::vector<std::string>(std::next( v.begin(), 2 ), std::next( v.begin(), 5 ) ) );
}
for ( const std::vector<std::string> &v : v2 )
{
for ( const std::string &s : v ) std::cout << s << ' ';
std::cout << std::endl;
}
It is simple to rewrite the code using the C++ 2003 syntax. For example you can write
std::vector<std::vector<std::string>> v1( N,
std::vector<std::string>( a, a + sizeof( a ) / sizeof( *a ) ) );
instead of
std::vector<std::vector<std::string>> v1( N, std::vector<std::string>( std::begin( a ), std::end( a ) ) );
and so on.
EDIT: If the columns are not adjacent then you can use the following approach
#include <iostream>
#include <vector>
#include <array>
#include <string>
#include <iterator>
#include <algorithm>
int main()
{
const size_t N = 10;
const size_t M = 3;
std::string a[N] = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J" };
std::vector<std::vector<std::string>> v1( N, std::vector<std::string>( std::begin( a ), std::end( a ) ) );
std::vector<std::vector<std::string>> v2;
v2.reserve( v1.size() );
std::array<std::vector<std::string>::size_type, M> indices = { 2, 5, 6 };
for ( const std::vector<std::string> &v : v1 )
{
std::vector<std::string> tmp( M );
std::transform( indices.begin(), indices.end(), tmp.begin(),
[&]( std::vector<std::string>::size_type i ) { return ( v[i] ); } );
v2.push_back( tmp );
}
for ( const std::vector<std::string> &v : v2 )
{
for ( const std::string &s : v ) std::cout << s << ' ';
std::cout << std::endl;
}
}