Insert an element into a range - c++

What is the right implementation of Insert method in the code below?
#include <ranges>
#include <vector>
#include <set>
template <std::ranges::range Range>
class Processor
{
public:
using T = std::ranges::range_value_t<Range>;
void Insert(Range range, T val)
{
//add val into range
}
};
int main()
{
std::vector<int> v;
Processor<std::vector<int>> p;
p.Insert(v, 5);
std::set<int> set;
Processor<std::set<int>> p1;
p.Insert(set, 5);
return 0;
}
Is it possible to insert to vector and set with the same code? (insertion into vector is probably push_back)

Is it possible to insert to vector and set with the same code? (insertion into vector is probably push_back)
If you want to call the same function for set and vector you could try this soultion using templates, constexpr if and std::same_as.
example:
#include <ios>
#include <iostream>
#include <set>
#include <type_traits>
#include <vector>
template <typename Range, typename T>
void
insertInSetOrVector (Range &range, T const &t)
{
if constexpr (std::same_as<std::vector<T>, Range>)
{
range.push_back (t);
}
else if constexpr (std::same_as<std::set<T>, Range>)
{
range.insert (t);
}
}
int
main ()
{
auto vec = std::vector<int>{};
insertInSetOrVector (vec, 42);
auto set = std::set<int>{};
insertInSetOrVector (set, 42);
std::cout << "vector: " << vec.at (0) << std::endl;
std::cout << std::boolalpha << "set has 42: " << (set.count (42) == 1) << std::endl;
return 0;
}
which prints:
vector: 42
set has 42: true
edit: thanks for the suggestion (set.count (42) == 1) is more accurate.

Related

Finding the top K frequent Elements

I am trying to solve a question on leetcode which is finding the top k frequent elements. I think my code is correct but the output for a test case is failing.
Input: [ 4,1,-1,2,-1,2,3]
K=2
My answer comes out to be {1,-1} but the expected is {-1,2}. I am not sure where am i getting wrong.
struct myComp{
constexpr bool operator()(pair<int,int> & a,pair<int,int> &b)
const noexcept
{
if(a.second==b.second)
{
return a.first<b.first;
}
return a.second<b.second;
}
};
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int,int> mp;
for(int i=0;i<nums.size();i++)
{
mp[nums[i]]++;
}
priority_queue<pair<int,int>,vector<pair<int,int>>,myComp> minheap;
for(auto x:mp)
{
minheap.push(make_pair(x.second,x.first));
if(minheap.size()>k)
{
minheap.pop();
}
}
vector<int> x;
while(minheap.size()>0)
{
x.push_back(minheap.top().second);
minheap.pop();
}
return x;
link: https://leetcode.com/problems/top-k-frequent-elements
In the minheap, pairs of <frequency, element> are being pushed. Since we want to sort these pairs on basis of frequency, we need to compare on the basis of frequency only.
Let's say there are two pairs a and b. Then for normal sorting, the comparison would be :
a.first < b.first;
And for reverse sorting, the comparison would be :
a.first > b.first;
In case of min-heap, we need reverse sorting. Hence, the following comparator makes your code pass all the test cases :
struct myComp
{
constexpr bool operator()(pair<int,int> & a,pair<int,int> &b)
const noexcept
{
return a.first > b.first;
}
};
There are several issues with your code.
Obviously there is somewhere using namespace std; in your code. This should be avoided. You will find many posts here on SO explaining, why it this should not be done.
Then we need to qualify all elements from the std library with std::, which makes the scope very clear.
Next: You do not need your own sorting function. Since you insert the elements from the pair in swapped order into the std::priority_queue, the sorting criteria is valid for the counter part, not for the key value. So, your sorting function was anyway wrong, because it was sorting accodring to "second" and not to "first". But if we have a standard sorting, we do not need a special sorting algorithm. A std::pair has a less-than operator. So, the definition can be simply:
std::priority_queue<std::pair<int, int>> minheap;
Then, your if statement
if(minheap.size()>k)
{
minheap.pop();
}
is wrong. You will allow only k values to be inserted. And this are not necessarily the biggest ones. So, you need to insert all values from the std::unordered map. And then they are sorted.
With some cosmetic changes the code will look like the below:
#include <iostream>
#include <utility>
#include <unordered_map>
#include <vector>
#include <queue>
std::vector<int> topKFrequent(std::vector<int>& nums, size_t k) {
std::unordered_map<int, int> mp;
for (size_t i = 0; i < nums.size(); i++)
{
mp[nums[i]]++;
}
std::priority_queue<std::pair<int, int>> minheap;
for (auto x : mp)
{
minheap.push(std::make_pair(x.second, x.first));
}
std::vector<int> x;
for (size_t i{}; i< k; ++i)
{
x.push_back(minheap.top().second);
minheap.pop();
}
return x;
}
int main() {
std::vector data{ 4,1,-1,2,-1,2,3 };
std::vector result = topKFrequent(data, 2);
for (const int i : result) std::cout << i << ' '; std::cout << '\n';
return 0;
}
An additional solution
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <utility>
auto topKFrequent(std::vector<int>& nums, size_t k) {
// Count occurences
std::unordered_map<int, size_t> counter{};
for (const int& i : nums) counter[i]++;
// For storing the top k
std::vector<std::pair<int, size_t>> top(k);
// Get top k
std::partial_sort_copy(counter.begin(), counter.end(), top.begin(), top.end(),
[](const std::pair<int, size_t >& p1, const std::pair<int, size_t>& p2) { return p1.second > p2.second; });
return top;
}
// Test code
int main() {
std::vector data{ 4,1,-1,2,-1,2,3 };
for (const auto& p : topKFrequent(data, 2))
std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n';
return 0;
}
And of course, we do have also the universal solution for any kind of iterable container. Including the definition for type traits using SFINAE and checking for the correct template parameter.
#include <iostream>
#include <utility>
#include <unordered_map>
#include <algorithm>
#include <vector>
#include <iterator>
#include <type_traits>
// Helper for type trait We want to identify an iterable container ----------------------------------------------------
template <typename Container>
auto isIterableHelper(int) -> decltype (
std::begin(std::declval<Container&>()) != std::end(std::declval<Container&>()), // begin/end and operator !=
++std::declval<decltype(std::begin(std::declval<Container&>()))&>(), // operator ++
void(*std::begin(std::declval<Container&>())), // operator*
void(), // Handle potential operator ,
std::true_type{});
template <typename T>
std::false_type isIterableHelper(...);
// The type trait -----------------------------------------------------------------------------------------------------
template <typename Container>
using is_iterable = decltype(isIterableHelper<Container>(0));
// Some Alias names for later easier reading --------------------------------------------------------------------------
template <typename Container>
using ValueType = std::decay_t<decltype(*std::begin(std::declval<Container&>()))>;
template <typename Container>
using Pair = std::pair<ValueType<Container>, size_t>;
template <typename Container>
using Counter = std::unordered_map<ValueType<Container>, size_t>;
// Function to get the k most frequent elements used in any Container ------------------------------------------------
template <class Container>
auto topKFrequent(const Container& data, size_t k) {
if constexpr (is_iterable<Container>::value) {
// Count all occurences of data
Counter<Container> counter{};
for (const auto& d : data) counter[d]++;
// For storing the top k
std::vector<Pair<Container>> top(k);
// Get top k
std::partial_sort_copy(counter.begin(), counter.end(), top.begin(), top.end(),
[](const std::pair<int, size_t >& p1, const std::pair<int, size_t>& p2) { return p1.second > p2.second; });
return top;
}
else
return data;
}
int main() {
std::vector testVector{ 1,2,2,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,6,7 };
for (const auto& p : topKFrequent(testVector, 2)) std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n';
std::cout << '\n';
double cStyleArray[] = { 1.1, 2.2, 2.2, 3.3, 3.3, 3.3 };
for (const auto& p : topKFrequent(cStyleArray, 2)) std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n';
std::cout << '\n';
std::string s{"abbcccddddeeeeeffffffggggggg"};
for (const auto& p : topKFrequent(s, 2)) std::cout << "Value: " << p.first << " \t Count: " << p.second << '\n';
std::cout << '\n';
double value = 12.34;
std::cout << topKFrequent(value,2) << "\n";
return 0;
}
Developed and tested with Microsoft Visual Studio Community 2019, Version 16.8.2.
Additionally compiled and tested with clang11.0 and gcc10.2
Language: C++17

What is this error? "no matching function for call to 'foreach(std::array<int, 4>&, void(&)(int))"

I created a foreach() function, and it should print values of an array, but it tells me this:
no matching function for call to 'foreach(std::array<int, 4>&, void(&)(int))'
And also:
mismatched types 'unsigned int' and 'long unsigned int'
But when I try to use vectors instead of arrays, or on line 11 use template<unsigned int N> instead of unsigned int, if I use long unsigned int, it works fine.
So, why do I need to use long unsigned int?
And what does the "no matching function" error mean with arrays?
#include<iostream>
#include<string>
#include<array>
typedef void(*func)(int);
void print(int value) {
std::cout << "value is : " << value << std::endl;
}
template<unsigned int N>
void foreach(std::array<int, N>& values, func print) {
int value;
for(int i = 0; i < values.size(); i++) {
value = values[i];
print(value);
}
}
int main() {
std::array<int, 4> arr = { 0, 1, 2, 3 };
foreach(arr, print);
return 0;
}
With vectors:
#include<iostream>
#include<string>
#include<vector>
typedef void(*func)(int);
void print(int value) {
std::cout << "value is : " << value << std::endl;
}
void foreach(std::vector<int>& values, func print) {
int value;
for(int i = 0; i < values.size(); i++) {
value = values[i];
print(value);
}
}
int main() {
std::vector<int> v = { 0, 1, 2, 3 };
foreach(v, print);
return 0;
}
The template for std::array does not take an unsigned int, it takes a std::size_t, which may or may not (probably not) be defined as unsigned int:
template<size_t N>
void foreach(std::array<int, N>& values, func print);
A better option is to make your function be container-agnostic instead, by passing it iterators instead of the actual container, eg:
#include <iostream>
#include <string>
#include <array>
#include <vector>
typedef void(*func)(int);
void print(int value) {
std::cout << "value is : " << value << std::endl;
}
template<typename Iter>
void foreach(Iter begin, Iter end, func print) {
while (begin != end) {
print(*begin);
++begin;
}
}
int main() {
std::array<int, 4> arr = { 0, 1, 2, 3 };
foreach(arr.begin(), arr.end(), print);
std::vector<int> v = { 0, 1, 2, 3 };
foreach(v.begin(), v.end(), print);
return 0;
}
Not only does this allow the function to work with multiple containers, but also with different element types, if you change the print parameter to be a template as well, eg:
#include <iostream>
#include <string>
#include <array>
#include <vector>
void printInt(int value) {
std::cout << "value is : " << value << std::endl;
}
void printStr(const std::string &value) {
std::cout << "value is : " << value << std::endl;
}
template<typename Iter, typename Callable>
void foreach(Iter begin, Iter end, Callable print) {
while (begin != end) {
print(*begin);
++begin;
}
}
int main() {
std::array<int, 4> arr = { 0, 1, 2, 3 };
foreach(arr.begin(), arr.end(), printInt);
std::vector<std::string> v = { "hello", "world", "joe", "smoe" };
foreach(v.begin(), v.end(), printStr);
return 0;
}
This is the exact strategy that standard algorithms use, such as std::for_each() (which you should be using instead of writing your own) 1, eg:
#include <iostream>
#include <string>
#include <array>
#include <vector>
#include <algorithm>
int main() {
std::array<int, 4> arr = { 0, 1, 2, 3 };
std::for_each(arr.begin(), arr.end(),
[](int value) { std::cout << "value is : " << value << std::endl; }
);
std::vector<std::string> v = { "hello", "world", "joe", "smoe" };
std::for_each(v.begin(), v.end(),
[](const std::string &value) { std::cout << "value is : " << value << std::endl; }
);
return 0;
}
1: C++20 introduced a new Ranges library that has algorithms to act on whole containers.
Because the second template parameter for std::array is std::size_t, not unsigned int. Compiler cannot infer the convertion between types in template function. And it just happens that std::size_t in your compiler is a typedef on long unsigned int, so that's what it suggests.
You can make it work in two ways:
Change the template type to std::size_t
Provide template parameter explicitly when calling:
foreach<4>(v, print);

Vector of string shared memory map

How to append string to a vector contained inside map? Structure is map(float,vector(string)) where the map is in shared memory.my question is if key==desired key then append string to the vector of strings?
Do you mean something like this:
#include <map>
#include <vector>
#include <string>
#include <iostream>
int main()
{
std::map<float, std::vector<std::string>> m;
m[.5f].emplace_back("First");
m[.5f].emplace_back("Second");
m[.0f].emplace_back("Hello");
m[.0f].emplace_back("World");
for(const auto& [key, value] : m)
{
std::cout << "Key: " << key << '\n';
for(const auto& str : value)
std::cout << '\t' << str << '\n';
}
std::cout.flush();
return 0;
}
Doing this in shared memory is pretty hard, actually.
If you get all the allocators right, and add the locking, you'd usually get very clunky code that is hard to read due to all the allocator passing around.
You can, however, employ Boost's scoped allocator adaptor which will do a lot (lot) of magic that makes life better.
I think the following code sample just about nails the sweet spot.
Warning: This builds on years of experience trying to beat this into submission. If you fall just outside of the boundary of "magic" (mostly the in-place construction support due to uses_allocator<> and scoped_allocator_adaptor) you will find it breaks up and you'll be writing a lot of manual constructor/conversion calls to make it work.
Live On Coliru
#define DEMO
#include <iostream>
#include <iomanip>
#include <mutex>
#include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/managed_mapped_file.hpp> // For Coliru (doesn't support shared memory)
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/container/scoped_allocator.hpp>
namespace bip = boost::interprocess;
namespace bc = boost::container;
namespace Shared {
using Segment = bip::managed_mapped_file; // Coliru doesn't support bip::managed_shared_memory
template <typename T> using Alloc = bc::scoped_allocator_adaptor<bip::allocator<T, Segment::segment_manager> >;
template <typename V>
using Vector = bip::vector<V, Alloc<V> >;
template <typename K, typename V, typename Cmp = std::less<K> >
using Map = bip::map<K, V, Cmp, Alloc<std::pair<K const, V> > >;
using String = bip::basic_string<char, std::char_traits<char>, Alloc<char> >;
using Mutex = bip::interprocess_mutex;
}
namespace Lib {
using namespace Shared;
struct Data {
using Map = Shared::Map<float, Shared::Vector<Shared::String> >;
mutable Mutex _mx;
Map _map;
template <typename Alloc> Data(Alloc alloc = {}) : _map(alloc) {}
bool append(float f, std::string s) {
std::lock_guard<Mutex> lk(_mx); // lock
auto it = _map.find(f);
bool const exists = it != _map.end();
#ifndef DEMO
if (exists) {
it->second.emplace_back(s);
}
#else
// you didn't specify this, but lets insert new keys here, if
// only for the demo
_map[f].emplace_back(s);
#endif
return exists;
}
size_t size() const {
std::lock_guard<Mutex> lk(_mx); // lock
return _map.size();
}
friend std::ostream& operator<<(std::ostream& os, Data const& data) {
std::lock_guard<Mutex> lk(data._mx); // lock
for (auto& [f,v] : data._map) {
os << f << " ->";
for (auto& ss : v) {
os << " " << std::quoted(std::string(ss));
}
os << "\n";
}
return os;
}
};
}
struct Program {
Shared::Segment msm { bip::open_or_create, "data.bin", 10*1024 };
Lib::Data& _data = *msm.find_or_construct<Lib::Data>("data")(msm.get_segment_manager());
void report() const {
std::cout << "Map contains " << _data.size() << " entries\n" << _data;
}
};
struct Client : Program {
void run(float f) {
_data.append(f, "one");
_data.append(f, "two");
}
};
int main() {
{
Program server;
server.report();
Client().run(.5f);
Client().run(.6f);
}
// report again
Program().report();
}
First run would print:
Map contains 0 entries
Map contains 2 entries
0.5 -> "one" "two"
0.6 -> "one" "two"
A second run:
Map contains 2 entries
0.5 -> "one" "two"
0.6 -> "one" "two"
Map contains 2 entries
0.5 -> "one" "two" "one" "two"
0.6 -> "one" "two" "one" "two"

Displacing location while iterating through vector

How can you displace the location you iterate through a vector? I've tried something like:
for(auto x : vect+2)
but this doesn't work. I'm sure there's a simple resolve, but I haven't been able to find anything online.
If you want to use the range-based for, you could use Boost.Range to create a range that starts from the third element of your vector (begin() + 2):
for (auto x : boost::make_iterator_range(begin(v) + 2, end(v)))
{
std::cout << x << " ";
}
Here is a simple example:
#include <iostream>
#include <vector>
#include <algorithm>
#include <boost/range.hpp>
#include <boost/range/adaptors.hpp>
int main()
{
std::vector<int> v(10);
iota(begin(v), end(v), 1);
for (auto x : boost::make_iterator_range(begin(v) + 2, end(v)))
{
std::cout << x << " ";
}
}
If you want to loop through every second element, instead, you could change your range as follows:
namespace rng = boost::adaptors;
for (auto x : v | rng::strided(2))
{
std::cout << x << " ";
}
Which in a full program would be:
#include <iostream>
#include <vector>
#include <algorithm>
#include <boost/range.hpp>
#include <boost/range/adaptors.hpp>
int main()
{
namespace rng = boost::adaptors;
std::vector<int> v(10);
iota(begin(v), end(v), 1);
for (auto x : v | rng::strided(2))
{
std::cout << x << " ";
}
}
Boost.Range is pretty flexible, so you can for instance combine the two adapters above:
for (auto x : boost::make_iterator_range(begin(v) + 2, end(v)) |
rng::strided(3))
{
std::cout << x << " ";
}
If you do not want or cannot use Boost, you could use a classical for loop with iterators:
for (auto i = begin(v) + 2; i != end(v); ++i)
{
std::cout << *i << " ";
}
This is how the whole program would look like:
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> v(10);
iota(begin(v), end(v), 1);
for (auto i = begin(v) + 2; i != end(v); ++i)
{
std::cout << *i << " ";
}
}
This can be achieved very simply, and has a number of solutions to suit any programming style.
The Classical Approach
int main()
{
std::vector<int> v(10);
std::iota(v.begin(), v.end(), 1);
for (auto i = v.begin() + 2; i != v.end(); ++i)
{
std::cout << *i << " ";
}
}
The Functional Approach
int main()
{
std::vector<int> v(10);
std::iota(v.begin(), v.end(), 1);
std::for_each(v.begin() + 2, v.end(), [](int val)
{
std::cout << val << " ";
}
);
}
You could add a light wrapper to make it work with range-based for:
#include <iostream>
#include <vector>
#include <iterator>
namespace range
{
template <typename C>
struct make_range
{
C t;
make_range(C t, int offset)
: t(std::begin(t) + offset, std::end(t))
{}
auto begin() -> decltype(t.begin())
{
return t.begin();
}
auto end() -> decltype(t.end())
{
return t.end();
}
};
}
int main()
{
std::vector<int> v{1, 2, 3, 4, 5};
for (auto i : range::make_range<decltype(v)>(v, 2))
{
std::cout << i << std::endl;
}
}
Perhaps it would be better to just store the iterators:
namespace range
{
template <typename C>
struct make_range
{
typename C::iterator beg_iter;
typename C::iterator end_iter;
make_range(C& t, int offset)
: beg_iter(std::begin(t) + offset), end_iter(std::end(t))
{}
auto begin() -> decltype(beg_iter)
{
return beg_iter;
}
auto end() -> decltype(end_iter)
{
return end_iter;
}
};
}

How to sort a multiset to a container by the number of element occurences

I want to get the elements sorted by the number of their occurence.
This is what I have come up with (mHeights is a std::multiset):
namespace{
template<class U,class T>
class HistPair{
public:
HistPair(U count,T const& el):mEl(el),mNumber(count){
}
T const& getElement()const{return mEl;}
U getCount()const{return mNumber;}
private:
T mEl;
U mNumber;
};
template<class U,class T>
bool operator <(HistPair<U,T> const& left,HistPair<U,T> const& right){
return left.getCount()< right.getCount();
}
}
std::vector<HistPair<int,double> > calcFrequentHeights(){
typedef HistPair<int,double> HeightEl;
typedef std::vector<HistPair<int,double> > Histogram;
std::set<double> unique(mHeights.begin(),mHeights.end());
Histogram res;
boostForeach(double el, unique) {
res.push_back(HeightEl(el,mHeights.count(el)));
}
std::sort(res.begin(),res.end());
std::reverse(res.begin(),res.end());
return res;
}
So first I take all unique elements from the multiset, then I count them and sort them into a new container (I need the counts so I use a map). This looks quite complicated for such an easy task.
Apart from the HistPair, which is used elsewhere as well, isn't there any stl algorithm that would simplify this task e.g. using equal_range or sth. alike.
Edit: I need the number of occurences as well, sorry I forgot about that
This snippet does what you want, by combining an std::set, a lambda and std::multiset::count:
#include <iostream>
#include <set>
#include <vector>
#include <algorithm>
int main() {
std::multiset<int> st;
st.insert(12);
st.insert(12);
st.insert(12);
st.insert(145);
st.insert(145);
st.insert(1);
st.insert(2);
std::set<int> my_set(st.begin(), st.end());
std::vector<int> my_vec(my_set.begin(), my_set.end());
std::sort(my_vec.begin(), my_vec.end(),
[&](const int &i1, const int &i2) {
return st.count(i1) < st.count(i2);
}
);
for(auto i : my_vec) {
std::cout << i << " ";
}
std::cout << std::endl;
}
You might want to reverse the vector. This outputs:
1 2 145 12
Edit: Taking into account you also need the item count, this will do it:
#include <iostream>
#include <set>
#include <vector>
#include <algorithm>
int main() {
typedef std::vector<std::pair<int, int>> MyVector;
std::multiset<int> st;
st.insert(12);
st.insert(12);
st.insert(12);
st.insert(145);
st.insert(145);
st.insert(1);
st.insert(2);
std::set<int> my_set(st.begin(), st.end());
MyVector my_vec;
my_vec.reserve(my_set.size());
for(auto i : my_set)
my_vec.emplace_back(i, st.count(i));
std::sort(my_vec.begin(), my_vec.end(),
[&](const MyVector::value_type &i1, const MyVector::value_type &i2) {
return i1.second < i2.second;
}
);
for(const auto &i : my_vec)
std::cout << i.first << " -> " << i.second << std::endl;
}
Which outputs:
1 -> 1
2 -> 1
145 -> 2
12 -> 3