Assign numbers to characters C++ - c++

I need a way to assign numbers to letters in C++, for example, '$' would represent the number 1. I obviously need to be able to obtain the number from the character with something like a function, e.g. getNumFromChar('$') would return 1 and getNumFromChar('#') would return 2. Is there an easy and fast way to do this in C++?

The fastest way is to write a 256 entry lookup table, containing the mapped values in the character's ASCII index. This is similar to how isdigit and tolower work, for example:
int getNumFromChar(char c)
{
static const int table[256] = {/* table entries */};
return table[c & 0xff];
}

If you would like to assign the values yourself use a map and store your key to letter combinations. If you are ok with preassigned unique values mapped to each letter, and are only using ASCII characters, then type cast them to integers... ex) std::static_cast< int >('$');

Create a vector std::vector<int> v(256,0); which is indexed by your characters and initially all of their numbers are zeros that you could treat as invalid numbers. Finally assign for each 'numbered' character some number e.g. v['$'] = 1; v['#'] = 2; using a fact that characters are actually integers from 0 to 255.

As pointed out in the comments, you can use a std::map in the following way:
#include <iostream>
#include <map>
#include <cstring>
struct myComp
{
bool operator()(const char* s1, const char* s2) const
{
return strcmp(s1, s2) < 0;
}
};
int main()
{
std::map<const char*, int, myComp> test;
test["$"] = 1;
test["#"] = 2;
std::cout << "$ -> " << test["$"] <<"\n";
std::cout << "# -> " << test["#"] <<"\n";
return 0;
}
Live demo here.
Majority of the other answers will work only if you have a maximum of 256 values to be stored. However, using Maps, you can store just any number of elements.

A lot of people are suggesting std::map<char,int>, which is fine and works, but a faster (?) way of doing this with no dependencies is to just use a massive switch statement
int getNumFromChar(char c){
switch(c){
case '$':
return 1;
case '#':
return 2;
//etc
}
return -1; //just in case of error, for style points
}
Depending on how much you care about performance/memory usage and how many case statements you'd have to write, this may or may not be a viable way to do what you want. Just thought I'd throw it out there since at the time of this writing I don't believe anyone has.
EDIT: Also, depending on the frequency of use of each individual character, and if you know the entire mapping before using this function or if you ever change the mapping, a std::map is way better, but I believe this is faster otherwise.

You could do something like this:
#include <map>
#include <iostream>
#include <exception>
typedef std::map<char, int> easymap_type;
class EasyMap {
public:
EasyMap() {}
virtual ~EasyMap() {}
void assign_int_to_char(const int& i, const char& c)
{
_map[c] = i;
}
int get_int_from_char(const char& c) const
{
easymap_type::const_iterator it = _map.find(c);
if (it == _map.end())
{
std::cerr << "EasyMap Error: uninitialized key - '" << c << "'" << std::endl;
throw std::exception();
}
return it->second;
}
private:
easymap_type _map;
};
int main()
{
EasyMap ezmap;
ezmap.assign_int_to_char(42, 'a');
std::cout << "EasyMap[a] = " << ezmap.get_int_from_char('a') << std::endl;
std::cout << "EasyMap[b] = " << ezmap.get_int_from_char('b') << std::endl;
return 0;
}
I handled an uninitizialized key by throwing an exception, but you could do it different ways.

If your compiler support c++11 feature,you can use std::unordered_map as container to store char and double like std::unordered_map<char,double>.
Unordered map is an associative container that contains key-value pairs with unique keys. Search, insertion, and removal of elements have average constant-time complexity.In your problem char is the key and double is your value,char-double must be the key-value stored in container.

There are already a lot of reasonable answers... I prefer the static_cast<int>('#')
And there always has to be the most stupid useless compile time template idea about how to solve a problem.
I know it's stupid, and I'm not good at this kind of things, but here is my shot at it in c++11. Don't take me seriously. I just had to do something dumb.
#include <string>
#include <array>
#include <utility>
#include <iostream>
constexpr uint kNbChars {3};
constexpr std::array<std::pair<char, int>, kNbChars> kCharToInt {
std::make_pair('$', 1)
, std::make_pair('#', 2)
, std::make_pair('#', 3)
};
template <char c>
int getInt()
{
for (auto pair : kCharToInt)
{
if (pair.first == c)
{ return pair.second; }
}
return -1;
}
int main()
{
std::cout << getInt<'#'>() << std::endl;
std::cout << getInt<'g'>() << std::endl;
}
I think you can make getInt() constexpr too in c++14, but I may be wrong and cannot check it right now.
Of course it really is useless since you have to know the letter at compile time, but you could work around that by, well, just not making getInt a template function...

Related

How to search in a Set of vectors of integers

I am trying to form a set of vectors of integers and on checking if the same solution already exists in the set, I am not getting correct answer.
This is in regards to C++11. I had posted a similar kind of query earlier as well but had not got any meaningful replies.
Why is it that whenever we form a map or set of vectors, is is not able to recognize if I insert a vector which is identical to the one I have already inserted ?
I have been searching for an answer since months. Also, since this behavior is allowed in other languages like Java, there must be a work around this. It would be great if someone can point out why this behavior isn't working the way I expect it to and what should be the probable solution to this.
The code below is a solution to 3Sum problem on Leetcode, but doesn't work because of exactly what I have explained above.
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>>result;
unordered_map<int,int>m;
set<vector<int>>res;
bool flag=false;
if(nums.size()<=2)
return result;
vector<int>temp;
for(int i=0;i<nums.size()-1;i++)
{
int comp=-(nums[i]+nums[i+1]);
if(m.find(comp)!=m.end())
{
auto location=m.find(comp);
temp.push_back(comp);
temp.push_back(nums[i]);
temp.push_back(nums[i+1]);
if(res.find(temp)==res.end())
{
res.insert(temp);
result.push_back(temp);
}
temp.clear();
}
else
{
m[nums[i]]=i+1;
m[nums[i+1]]=i+2;
}
}
return result;
}
On giving input as
[0,0,0,0]
Answer should be:
[0,0,0]
Whereas I get :
[[0,0,0], [0,0,0]]
You could use tuples in the set instead of vectors.
#include <tuple>
#include <set>
#include <iostream>
using std::get;
int main(int argc, char* argv[]) {
std::set<std::tuple<int,int,int>> sums;
auto tup1 = std::make_tuple(0, 0, 0);
sums.insert(tup1);
auto tup2 = std::make_tuple(0,0,0);
sums.insert(tup2);
std::cout << sums.size() << std::endl;
for (auto& item : sums) {
std::cout << "(" << get<0>(item) << "," << get<1>(item) << "," << get<2>(item) << ")\n";
}
return 0;
}

C++ - STL - Vector - Why there is no facility to indicate relocation_count in the vector

It is quite surprising that given that reserving size for a vector in anticipation helps improve the performance of the application and
ensures that costly relocations do not occur when it gets filled
to its capacity why there is no facility given to get a relocation_count
at any given time , this may very much help programmer track optimal
size to be allocated to vector in cases where the exact capacity may
need to be determined from average over period of observations as
exact figure may not be known upfront.
To count re-allocations of a std::vector, the std::vector (or at least the write access methods of it) might be wrapped into a helper class.
Sample code:
#include <iostream>
#include <vector>
template <typename VALUE>
struct AllocCounter {
std::vector<VALUE> &vec;
unsigned n;
AllocCounter(std::vector<VALUE> &vec): vec(vec), n(0) { }
void push_back(const VALUE &value)
{
size_t old = vec.capacity();
vec.push_back(value);
n += old != vec.capacity();
}
};
int main()
{
std::vector<int> values;
AllocCounter<int> countAllocs(values);
for (int i = 1; i <= 1024; ++i) {
unsigned nOld = countAllocs.n;
countAllocs.push_back(i);
if (countAllocs.n > nOld) std::cout << 'R';
std::cout << '.';
}
std::cout << '\n'
<< "Number of (re-)allocations: " << countAllocs.n << '\n';
// done
return 0;
}
Output:
R.R.R..R....R........R................R................................R................................................................R................................................................................................................................R................................................................................................................................................................................................................................................................R................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
Number of (re-)allocations: 11
Live Demo on coliru
This sample is rather a proof of concept as it doesn't consider std::vector::emplace(), std::vector::resize(), etc.
Btw. if std::vector::push_back() is called directly the counting is by-passed (and may "overlook" re-allocations).
Using a custom allocator could solve this limitation.

How to say if something is in something else c++

I was wondering if there is an easy way to write this if statement in c++.
string c="B";
if(c=="B"||c=="X"||c=="I")
{
//stuff
}
for example,
string c="B";
if(c in {"b","X","I"})
{
//stuff
}
You can use the std:: find function to search your array.Suppose your array is arr=["C","X","I"] :
tofind string c="C"
For example your statement will change to:-
if(find(arr.begin(),arr.end(),c)!=arr.end())
{
//found do something
}
There is no "in" in C++
There is no direct support in language for this, but you can emulate it using function. For example, let us define a function that accept a string and a vector of strings to be compared:
bool in(const std::string& s, std::vector<std::string> v)
{
for (auto&& i : v)
if ( s == i)
return true;
return false;
}
now you can use this function directly in you if statement, using an initilizer list:
int main()
{
std::string c = "B";
if ( in(c, {"C","X","I", "B"}) )
std::cout << "found\n";
else
std::cout << "not found\n";
}
If doing multiple searches, std::set or std::unordered_map (C++11 hash table) will be better performance-wise than linear search through an array with std::find.
std::set<std::string> data_set{"B","X","I"};
//...somewhere else
bool has_c = data_set.count(c)>0;
Both std::set and std::unordered_maphave a count function that makes testing for a value pretty clean looking, i.e. avoiding iterators.
Program
Here's a full working program,
#include <string>
#include <set>
#include <iostream>
using namespace std;
int main()
{
set<string> data_set{"C","X","I"}
//...somewhere else
string c="B";
if( data_set.count(c)>0 )
{
cout << "here\n";
}
else
{
cout << "not\n";
}
}
Don't forget to set C++11 standard when you compile, e.g. g++ -std=c++11 main.cc.

Use for_each or accumulate to calculate frequencies

I've been playing with a simple example using C++11 and some standard algorithms, and I'm not sure whether to use std::accumulate or std::for_each. The problem is to count letters in a word, so, for example, for an input of "abracadabra", you get
'a' => 5
'b' => 2
'c' => 1
'd' => 1
'r' => 2
My first cut was to use std::accumulate. The reason this seemed natural is that we're really accumulating a value (a set of frequencies). Also I've been doing some functional programming recently and accumulate seemed to be the natural translation of folding a list.
vector<int> charsInWord(const string& text)
{
return
std::accumulate(text.begin(), text.end(), vector<int>(256),
[] (const vector<int>&v, char c)
{
vector<int> v2(v);
v2[c]++;
return v2;
} );
}
However this solution seemed rather cumbersome and took a little while to get right. Moreover, even with the new move semantics I couldn't quite convince myself that there wouldn't be any unnecessary copying.
So I went for for_each instead.
vector<int> charsInWord2(const string& text)
{
vector<int> charCounts(256);
std::for_each(text.begin(), text.end(),
[&] (char c)
{
charCounts[c]++;
} );
return charCounts;
}
This is probably easier to write and understand, and I certainly feel happier about its efficiency (although I miss the declarative, functional style of accumulate).
Is there any good reason to prefer one over the other in examples like these? From the comments and answers so far, it seems like if the value I am accumulating is non-trivial, say an stl container rather than an int, I should always prefer for_each, even when I am really "accumulating".
For the sake of completeness, the rest of the code to get this to compile and test is below
#include <string>
#include <vector>
#include <numeric> // accumulate
#include <algorithm> // for_each
using std::string;
using std::vector;
#include <iostream>
// ... insert code above ...
int main(int argc, char* argv[])
{
const vector<int> charCounts = charsInWord("abracadabra");
for(size_t c=0; c<charCounts.size(); ++c) {
const int count = charCounts[c];
if (count > 0) {
std::cout << "'" << static_cast<char>(c) << "'" << " => " << count << "\n";
}
}
return 0;
}
Personally I would not have written the accumulate like that:
vector<int> charsInWord(const string& text)
{
std::vector<int> result(256); // One version never copied.
int count = std::accumulate(text.begin(), text.end(), 0,
[&result] (int count, char c)
// ^^^^^^^^^ capture
{
result[c]++;
return count+1;
} );
// Might use count in the log file.
return result;
}
But If I am doing that it seems just as easy to use for_each()
vector<int> charsInWord2(const string& text)
{
vector<int> result(256);
std::for_each(text.begin(), text.end(),
[&result] (char c)
{
result[c]++;
} );
return result;
}
I don't see anything wrong with the for_each version.
But why not go with a simple for() loop?
vector<int> charsInWord2(const string& text)
{
vector<int> result(256);
for(char c : text) {result[c]++;}
return result;
}
There was some discussion about using std::map in the comments (and then in some deleted questions). Just to capture that here and expand.
We could have used std::map<char,int> instead of vector<int>. The difference are:
From: #Dave std::map has O(ln(n)) lookup time while vector is O(1). So there is a performance consideration. Note also that the fixed cost for map will be higher than vector. Though this is small but worth noting.
From: #Dave std::vector has a fixed size of approx 256*4 (1024), while map has a size of approx 12*number of unique characters (min 12 max 3072). So no real space consideration in modern machine. But may be worth optimizing on phones and such.
From: #POW The third point is the std::map makes printing the result much easier as you do not need to check for empty values.
Vector print
for(size_t c=0; c<charCounts.size(); ++c) {
if (count > 0) {
std::cout << "'" << static_cast<char>(c) << "' => " << charCounts[c] << "\n";
}
}
Map Print
for(auto loop: charCounts) {
std::cout << "'" << loop.first << "' => " << loop.second << "\n";
}

C++ empty and array index

Is it possible to do something like:
string word = "Hello";
word[3] = null;
if(word[3] == null){/.../}
in C++, basically making an array element empty. For example if I wanted to remove the duplicate characters from the array I'd set them to null first and then shifted the array to the left every time I found an array index that contained null.
If this is not possible what's a good way of doing something like this in C++ ?
If you want to remove adjacent duplicate characters, you can do this:
std::string::iterator new_end = std::unique(word.begin(), word.end());
word.erase(new_end, word.end());
If you want to mark arbitrary characters for removal, you can skip the marking and just provide the appropriate predicate to std::remove_if:
new_end = std::remove_if(word.begin(), word.end(), IsDuplicate);
word.erase(new_end, word.end());
However, I can't think of an appropriate predicate to use here that doesn't exhibit undefined behavior. I would just write my own algorithm:
template<typename IteratorT>
IteratorT RemoveDuplicates(IteratorT first, IteratorT last)
{
typedef typename std::iterator_traits<IteratorT>::value_type
ValueT;
std::map<ValueT, int> counts;
for (auto scan=first; scan!=last; ++scan)
{
++counts[*scan];
if(counts[*scan] == 1)
{
*first = std::move(*scan);
++first;
}
}
return first;
}
Or, if you don't care about the order of the elements, you could simply sort it, then use the first solution.
This is possible, since a single element of a string is an element within a char-array and thus representable as pointer, i. e. you can retrieve the address of the element. Therefore you can set word[3] = null. Your if-construct is valid but the compiler prints a warning, this is because NULL is only a pointer constant. Alternatives would be: if (!word[3]) or if(word[3] == 0).
But in any case you should consider using STL algorithms for removing duplicates.
I think you should take a look at the algorithm in the STL.
You are not very specific about what you want to remove but maybe this helps:
std::string string_with_dup("AABBCCDD");
std::string string_without_dup;
std::cout << string_with_dup << std::endl;
// with copy
std::unique_copy(string_with_dup.begin(), string_with_dup.end(), std::back_inserter(string_without_dup));
std::cout << string_without_dup << std::endl;
// or inplace
string_with_dup.erase(std::unique(string_with_dup.begin(), string_with_dup.end()), string_with_dup.end());
std::cout << string_with_dup << std::endl;
If you want to remove all duplicates (not only the adjacent ones, you should use the erase-remove idiom with something like this
#include <iostream>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
struct is_repeated {
is_repeated( map<char,int>& x ) :r(&x) {};
map<char,int>* r;
bool operator()( char c ) {
(*r)[c]++;
if( (*r)[c] > 1 )
return true;
return false;
}
};
int main (int argc, char**argv)
{
map<char,int> counter_map;
string v = "hello hello hello hello hello hello hello";
cout << v << endl;
is_repeated counter(counter_map);
v.erase( remove_if(v.begin(), v.end(), counter ), v.end() );
cout << v << endl;
}
outputs (as of this):
hello hello hello hello hello hello hello
helo