I want an array that have new attributed values if the value is x.
I can do that in PHP with that code:
$test = array(1=>55, 2=>66);
on above code if test[0] = 1, the new value of test[0] is going to be 55.
I want to do that in C++.
Normal C++ arrays do not have keys. There are always 0-indexed.
But we have std::map which is what PHP also use internally for key-based containers.
https://en.cppreference.com/w/cpp/container/map
You can make your own attributed values class in C++, so it has that behavior.
You can make it so the behavior is hard coded into the class. Or if you want to get fancier you can make the class having a value mapping passed in.
Here's an example with the value mapping passed into the class.
#include <cstddef>
#include <iostream>
#include <map>
#include <stdexcept>
#include <vector>
using sad_panda = std::logic_error;
using std::cout;
using std::map;
using std::size_t;
using std::vector;
namespace {
class AttributedValues {
vector<int> v;
map<int, int> attr_to_value;
public:
AttributedValues(map<int, int> mapping);
void set(size_t index, int value);
int operator[](size_t index) const;
};
AttributedValues::AttributedValues(map<int, int> mapping)
: attr_to_value{mapping}
{ }
void AttributedValues::set(size_t index, int value) {
if (index >= v.size()) {
v.resize(index+1);
}
auto iter = attr_to_value.find(value);
if (iter != attr_to_value.end()) {
value = iter->second;
}
v[index] = value;
}
int AttributedValues::operator[](size_t index) const {
if (index >= v.size()) {
throw sad_panda("AttributedValues::operator[] index out of range");
}
return v[index];
}
} // anon
int main() {
auto test = AttributedValues{{{1, 55}, {2, 66}}};
test.set(0, 1);
test.set(10, 2);
cout << test[0] << "\n";
cout << test[10] << "\n";
}
Related
#include<iostream>
using namespace std;
void reverse(int arr[],int n){
int start=0,end=n-1;
int temp;
while(start<=end){
temp=arr[end];
arr[end]=arr[start];
arr[start]=temp;
}
start++;
end--;
}
void print(int arr[],int n){
for(int i=0;i<n;i++){
cout<<arr;
}
cout<<endl;
}
int main(){
int arr[5]={1,2,3,4,5};
int brr[6]={3,6,8,2,1,0};
reverse(arr,5);
reverse(brr,6);
print(arr,5);
print(brr,6);
}
I am not able to run this code at any compiler, can anyone tell me where I am making the mistake
after running it I am getting nothing
I am not to happy about the material that is teaching you C++.
For one it still uses "C" style arrays, and it is learning you to write
using namespace std; which will lead to problems (name clashes) in big projects. So if you have a teacher tell him about this.
Here is a more C++ oriented example :
#include <algorithm>
#include <iostream>
#include <vector>
// Using namespace std; <-- No unlearn this
// don't use "C" style arrays, use std::array or std::vector
// "C" style arrays are inherently more "buggy" to use.
// for one it easy to get mismatch array size and the size you pass.
void reverse(std::vector<int>& values)
{
//std::reverse(values.begin(), values.end()); C++ has this out of the box for you
std::size_t left_pos = 0ul;
std::size_t right_pos = values.size() - 1; // last index in array
/// manually, you start with first and last element and swap them
// then move left position one to the right, and right position one to the left
// repeat until left_pos and right_pos cross.
for (; left_pos < right_pos; ++left_pos, --right_pos)
{
// c++ has a swap method to swap values
std::swap(values[left_pos], values[right_pos]);
}
}
// prints the content of the vector
std::ostream& operator<<(std::ostream& os, const std::vector<int>& values)
{
bool print_comma{ false };
for (const int value : values)
{
if (print_comma) std::cout << ",";
std::cout << value;
print_comma = true;
}
std::cout << "\n";
return os;
}
int main()
{
std::vector<int> arr1{ 1,2,3,4,5 };
reverse(arr1);
std::cout << arr1;
return 0;
}
Just for your information: I would recommend you to just use std::reverse() for the concrete example. Also, do not use raw arrays.
#include <algorithm>
#include <iostream>
template <typename T>
void print(const T& arr) {
for (auto &value: arr) {
std::cout << value << " ";
}
std::cout << "\n";
}
int main(){
std::array arr={1,2,3,4,5}; // Or just use a std::vector
std::array brr={3,6,8,2,1,0};
std::reverse(arr.begin(), arr.end());
std::reverse(brr.begin(), brr.end());
print(arr);
print(brr);
}
I keep getting a compilation error if I use an array passed as parameter to a method on std::begin or std::end such as this:
#include <iostream>
#include <algorithm>
using namespace std;
class Test
{
public:
static bool exists(int ints[], int size, int k)
{
if (std::find(std::begin(ints), std::end(ints), k) != std::end(ints)) {
return true;
}
return false;
}
};
I tried casting it to &int[0] but it will still not compile.
The parameter ints is not an array, so you aren't passing an array into std::begin / std::end in the example. A function parameter is never an array in C++. When you declare a function array to be an array such as in your example, that parameter is adjusted to be a pointer to the element of such array. In your case, the type of the parameter ints has been adjusted to be a pointer to int i.e. int*, and you cannot pass a pointer into std::begin / std::end, as the error message surely explains.
You have passed the size as another parameter, so you could instead use:
std::find(ints, ints + size, k)
A more modern API design is to wrap the pointer and the size into a single class. The C++20 standard library comes with such class template: std::span. It also has convenient range algorithms that are quite convenient:
static bool exists(std::span<const int> ints, int k)
{
if (std::ranges::find(ints, k) != std::end(ints)) {
Even moreso, C++20 standard library has a function template that makes your function unnecessary:
std::ranges::contains(some_array, k);
Use std::span.
try this:
#include <algorithm>
#include <iostream>
#include <span>
bool exists(std::span<int> span, int needle) {
return std::find(span.begin(), span.end(), needle) != span.end();
}
int main() {
int arr[] = {0, 1, 2, 3, 4};
bool ok = exists(arr, 3);
if (ok) {
std::cout << "ok" << '\n';
} else {
std::cout << "not ok" << '\n';
}
return 0;
}
Here is a working example.
#include <iostream>
#include <vector>
bool exists(int x[], const int size, const int k)
{
std::vector<int> v(x, x + size);
if (std::find( v.begin(), v.end(), k) != v.end()) {
return true;
}
return false;
}
int main()
{
int const sz = 10;
int arrayi[sz] = { 1, 2, 3,4 ,5 ,6 ,7 , 8, 9, 0 };
if (exists(arrayi, sz, 4))
std::cout << "exist" << std::endl;
else
std::cout << "it does not" << std::endl;
}
I'm trying to get the indices of one container where the elements match. Both containers are sorted in ascending order. Is there an algorithm or combo of algorithms that would place the indices of matching elements of sorted containers into another container?
I've coded an algorithm already, but was wondering if this has been coded before in the stl in some way that I didn't think of?
I would like the algorithm to have a running complexity comparable to the one I suggested, which I belive is O(min(m, n)).
#include <iterator>
#include <iostream>
template <typename It, typename Index_it>
void get_indices(It selected_it, It selected_it_end, It subitems_it, It subitems_it_end, Index_it indices_it)
{
auto reference_it = selected_it;
while (selected_it != selected_it_end && subitems_it != subitems_it_end) {
if (*selected_it == *subitems_it) {
*indices_it++ = std::distance(reference_it, selected_it);
++selected_it;
++subitems_it;
}
else if (*selected_it < *subitems_it) {
++selected_it;
}
else {
++subitems_it;
}
}
}
int main()
{
int items[] = { 1, 3, 6, 8, 13, 17 };
int subitems[] = { 3, 6, 17 };
int indices[std::size(subitems)] = {0};
auto selected_it = std::begin(items), it = std::begin(subitems);
auto indices_it = std::begin(indices);
get_indices(std::begin(items), std::end(items)
, std::begin(subitems), std::end(subitems)
, std::begin(indices));
for (auto i : indices) {
std::cout << i << ", ";
}
return 0;
}
We can use find_if to simplify the implementation of the function:
template<class SourceIt, class SelectIt, class IndexIt>
void get_indicies(SourceIt begin, SourceIt end, SelectIt sbegin, SelectIt send, IndexIt dest) {
auto scan = begin;
for(; sbegin != send; ++sbegin) {
auto&& key = *sbegin;
scan = std::find_if(scan, end, [&](auto&& obj) { return obj >= key; });
if(scan == end) break;
for(; scan != end && *scan == key; ++scan) {
*dest = std::distance(begin, scan);
++dest;
}
}
}
This doesn't make it that much shorter, but the code looks a little cleaner now. You're scanning until you find something as big as or equal to the key, and then you copy indicies to the destination as long as the source matches key.
maybe I misunderstodd the question. But there is a function in the algorithm library.
std::set_intersection
This does, what you want in one function. See:
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
int main()
{
// Input values
std::vector<int> items{ 1,3,6,8,13,17 };
std::vector<int> subitems{ 3,6,17 };
// Result
std::vector<int> result;
// Do the work. One liner
std::set_intersection(items.begin(),items.end(), subitems.begin(),subitems.end(),std::back_inserter(result));
// Debug output: Show result
std::copy(result.begin(), result.end(), std::ostream_iterator<int>(std::cout, " "));
return 0;
}
If I misunderstood, then please tell me and I will find another solution.
EDIT:
I indeed misunderstood. You wanted the indices. Then maybe like this?
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
using Iter = std::vector<int>::iterator;
int main()
{
// Input values
std::vector<int> items{ 1,3,6,8,13,17 };
std::vector<int> subitems{ 3,6,17 };
// Result
std::vector<int> indices{};
Iter it;
// Do the work.
std::for_each(subitems.begin(), subitems.end(), [&](int i) {it = find(items.begin(), items.end(), i); if (it != items.end()) indices.push_back(std::distance(items.begin(),it));});
// Debug output: Show result
std::copy(indices.begin(), indices.end(), std::ostream_iterator<int>(std::cout, " "));
return 0;
}
Unfortunately a very long "one-liner".
I need to think more . . .
The answer is yes but it will come with C++20:
you can use ranges for this purpose:
first make a view with some predicate you like:
auto result = items | ranges::view::filter(predicate);
then take the iterator to the original array from base, for example result.begin().base() will give you the iterator to the begin of result in the original array.
#include <algorithm>
#include <iostream>
#include <vector>
#include <iterator>
#include <range/v3/view/filter.hpp>
#include <range/v3/view/transform.hpp>
int main()
{
std::vector<int> items = { 1, 3, 6, 8, 13, 17 };
std::vector<int> subitems = { 3, 6, 17 };
auto predicate = [&](int& n){
for(auto& s : subitems)
if(n == s)
return true;
return false;
};
auto result = items | ranges::view::filter(predicate);
for (auto& n : result)
{
std::cout << n << '\n';
}
for(auto it = result.begin(); it != result.end(); ++it )
std::cout << it.base() - items.begin() << ' ';
}
see the godbolt
By using std::set_intersection, defining an assignment_iterator class and a assignment helper, this is possible:
#include <iterator>
#include <iostream>
#include <algorithm>
#include <vector>
template <typename Transform>
class assignment_iterator
{
Transform transform;
public:
using iterator_category = std::output_iterator_tag;
using value_type = void;
using difference_type = void;
using pointer = void;
using reference = void;
assignment_iterator(Transform transform)
: transform(transform)
{}
// For some reason VC++ is assigning the iterator inside of std::copy().
// Not needed for other compilers.
#ifdef _MSC_VER
assignment_iterator& operator=(assignment_iterator const& copy)
{
transform.~Transform();
new (&transform) Transform(copy.transform);
return *this;
}
#endif
template <typename T>
constexpr assignment_iterator& operator=(T& value) {
transform(value);
return *this;
}
constexpr assignment_iterator& operator* ( ) { return *this; }
constexpr assignment_iterator& operator++( ) { return *this; }
constexpr assignment_iterator& operator++(int) { return *this; }
};
template <typename Transform>
assignment_iterator<Transform> assignment(Transform&& transform)
{
return { std::forward<Transform>(transform) };
}
int main()
{
int items[] = { 1, 3, 6, 8, 13, 17 };
int subitems[] = { 3, 6, 17 };
std::vector<int> indices;
std::set_intersection(std::begin(items), std::end(items)
, std::begin(subitems), std::end(subitems)
, assignment([&items, &indices](int& item) {
return indices.push_back(&item - &*std::begin(items));
})
);
std::copy(indices.begin(), indices.end()
, assignment([&indices](int& index) {
std::cout << index;
if (&index != &std::end(indices)[-1])
std::cout << ", ";
})
);
return 0;
}
Demo
It's more code, but maybe assignment is a more generic means to do other operations, that currently require a specific implementations like back_inserter and ostream_iterator, and thus be less code in the long run (e.g. like the other use above with std::copy)?
This should work properly all the time based on the documentation here:
elements will be copied from the first range to the destination range.
You can use std::find and std::distance to find the index of the match, then put it in the container.
#include <vector>
#include <algorithm>
int main ()
{
std::vector<int> v = {1,2,3,4,5,6,7};
std::vector<int> matchIndexes;
std::vector<int>::iterator match = std::find(v.begin(), v.end(), 5);
int index = std::distance(v.begin(), match);
matchIndexes.push_back(index);
return 0;
}
To match multiple elements, you can use std::search in similar fashion.
I am trying to achieve the creation of such a map. The following code attempts to do this
#include <list>
#include <map>
#include <string>
class IntWithString {
private:
int a;
std::string s;
public:
IntWithString(int a, std::string s) : a(a), s(s) {}
std::string getString() { return s; }
int getInt() { return a; }
};
namespace {
std::map<std::string, std::list<IntWithString *> > m;
}
void appendMap(IntWithString *a) {
auto it = m.find(a->getString());
if (it != m.end()) {
m[a->getString()].push_back(a);
} else {
std::list<IntWithString *> l;
l.push_back(a);
m[a->getString()] = l;
}
}
int main() {
IntWithString a(10, "ten");
IntWithString b(11, "ten");
appendMap(&a);
appendMap(&b);
return 0;
}
However when looking at the map m with the debugger I am getting a map that maps "ten" to a list of size 0. What I would like is a list of size 2.
I am not sure what you mean. If I do:
std::cout << m.size() << ", " << m["ten"].size() << std::endl;
I get this output:
1, 2
which is a map with one key ("ten"), and two values for that key (10 and 11), as expected.
Live demo
PS: Storing pointers in a container like this is a bit uncommon in C++. If you really want to do this though, consider to use Smart Pointers.
The error reads:
request for member 'begin', 'end' in 'arr' which is non class type int[5],
unable to deduce from expression error.
My code:
#include <iostream>
using namespace std;
int main()
{
int * mypointer;
int arr[5] = {1,3,5,7,9};
mypointer = arr;
for(auto it = arr.begin(); it != arr.end(); ++it) {
cout<<*mypointer<<endl;
mypointer++;
}
return 0;
}
Arrays have no member functions as they aren't a class type. This is what the error is saying.
You can use std::begin(arr) and std::end(arr) from the <iterator> header instead. This also works with types that do have .begin() and .end() members, via overloading:
#include <array>
#include <vector>
#include <iterator>
int main()
{
int c_array[5] = {};
std::array<int, 5> cpp_array = {};
std::vector<int> cpp_dynarray(5);
auto c_array_begin = std::begin(c_array); // = c_array + 0
auto c_array_end = std::end(c_array); // = c_array + 5
auto cpp_array_begin = std::begin(cpp_array); // = cpp_array.begin()
auto cpp_array_end = std::end(cpp_array); // = cpp_array.end()
auto cpp_dynarray_begin = std::begin(cpp_dynarray); // = cpp_dynarray.begin()
auto cpp_dynarray_end = std::end(cpp_dynarray); // = cpp_dynarray.end()
}
For a standard fixed-length C array, you can just write
int c_array[] = {1,3,5,7,9}, acc = 0;
for (auto it : c_array) {
acc += it;
}
The compiler does the behind-the-scenes work, eliminating the need to create all those begin and end iterators.
In C++, arrays are not classes and therefore do not have any member methods. They do behave like pointers in some contexts. You can take advantage of this by modifying your code:
#include <iostream>
using namespace std;
int main()
{
int * mypointer;
const int SIZE = 5;
int arr[SIZE] = {1,3,5,7,9};
mypointer = arr;
for(auto it = arr; it != arr + SIZE; ++it) {
cout<<*mypointer<<endl;
mypointer++;
}
return 0;
}
Of course, this means that mypointer and it both contain the same address, so you don't need both of them.
One thing I'd like to point out for you is that you really don't have to maintain a separate int* to use in dereferencing the array elements, apart from the whole member thing others have well pointed out.
Using a more modern approach, the code is both more readable, as well as safer:
#include <iostream>
#include <algorithm>
#include <array>
#include <iterator>
using namespace std;
int main()
{
std::array<int, 5> cpp_array{1,3,5,7,9};
// Simple walk the container elements.
for( auto elem : cpp_array )
cout << elem << endl;
// Arbitrary element processing on the container.
std::for_each( begin(cpp_array), end(cpp_array), [](int& elem) {
elem *= 2; // double the element.
cout << elem << endl;
});
}
Using the lambda in the second example allows you to conveniently perform arbitrary processing on the elements, if needed. In this example, I'm just showing doubling each element, but you can do something more meaningful within the lambda body instead.
Hope this makes sense and helps.
Perhaps here is a cleaner way to do it using templates and lambdas in c++14:
Define:
template<typename Iterator, typename Funct>
void my_assign_to_each(Iterator start, Iterator stop, Funct f) {
while (start != stop) {
*start = f();
++start;
}
}
template<typename Iterator, typename Funct>
void my_read_from_each(Iterator start, Iterator stop, Funct f) {
while (start != stop) {
f(*start);
++start;
}
}
And then in main:
int x[10];
srand(time(0));
my_assign_to_each(x, x+10, [] () -> int { int rn{}; rn = rand(); return rn; });
my_read_from_each(x, x+10, [] (int value) { std::cout << value << std::endl; });
int common_value{18};
my_assign_to_each(x, x+10, [&common_value] () -> int { return common_value; });
my_read_from_each(x, x+10, [] (int value) { std::cout << value << std::endl; });
Quite late but I think it's worth to mention that:
void findavgTime(int n)
{
int wt1[n];
fill_wt(wt1,n); //Any method that puts the elements into wt1
int wt2[3];
int sum = accumulate(begin(wt1), end(wt1), 0); // Fails but wt2[3] will pass. Reason: variable-sized array type ‘int [n]’ is not a valid template argument)
}