How to use std::find() with a 2d std: array? - c++

I am writing a function CreateGrid() that is trying to take a std::string and a 2d std::array as parameters, and put the individual characters from the std::string into the array. The rest of the array, if space is left, should be filled with the alphabetical characters A-Y, inclusive, in alphabetical order, providing they do not already appear in the std::string. Each character should only appear once in the final returned array.
So for input string keyword = "HELO";
The output would be:
H E L
O A B
C D F
Note the char 'E' only appears once.
I am trying to use std::find() to test whether all the chars in the alphabet A-Y (Z intentionally excluded) is already in the array, due to the keyword, before inserting it if it is not so that there are no duplicates. I am getting a compile error.
Code:
array<array<char,3>,3> CreateGrid(std::string keyword, array<array<char,3>,3> myArray)
{
char letterToFind = 'A';
for (int row = (keywordLength/3); row < 3; row++ )
{
for (int column = (keywordLength % 3); column < 3; column++)
{
auto it = std::find(begin(myArray), end(myArray), letterToFind);
if ( it == end(myArray) ) // value not in array
{
myArray[row][column] = letterToFind; //insert
}
letterToFind++;
}
}
return myArray;
}
int main()
{
array<array<char,3>,3> myArray = { {'H','E','L'}, {'O','+','+'}, {'+','+','+'} };
CreateGrid("HELO", myArray);
return 0;
}
I get a compile error for this line:
auto it = std::find(begin(myArray), end(myArray), letterToFind);
message : see reference to function template instantiation '_InIt std::find<std::_Array_iterator<_Ty,5>,char>(_InIt,const _InIt,const char &)' being compiled with[_InIt=std::_Array_iterator<std::array<char,3>3>,_Ty=std::array<char,3>]
How do I check to see if a char is in the array already, and if not then add it?

This is not normally trivial since you have nested arrays. std::find can only return an iterator to the outer array, which isn't useful if you need to change the value in the inner array.
However, I notice that you don't actually use it other than to see if the sought element was located. So, we can redefine the operation: instead of finding the element in an inner array that contains the value, it's sufficient to find any inner array that contains the value you're looking for:
auto it = std::find(
begin(myArray), end(myArray),
[letterToFind](array<char,3> const & inner) {
return std::find(begin(inner), end(inner), letterToFind) != end(inner);
}
);
If you found such an inner array, then you know that the value exists in the 2d array and can proceed.
Since you don't need the iterator, you could also use std::any_of instead:
bool found = std::any_of(
begin(myArray), end(myArray),
[letterToFind](array<char,3> const & inner) {
return std::find(begin(inner), end(inner), letterToFind) != end(inner);
}
);

Your myArray parameter is an array containing array<char,3> elements. You are using find() to search only that outer array. So begin(myArray) will return an iterator that enumerates array<char,3> elements, and you can't compare a single char to a whole array<char,3>, which is why your find() call does not compile. You would have to loop though the outer array calling find() on each inner array instead.
A simpler way to implement this is to use a lookup table to keep track of the characters that you have already seen, eg:
const size_t MaxRows = 3;
const size_t MaxColumns = 3;
const size_t MaxCells = MaxRows * MaxColumns;
using gridRow = std::array<char, MaxColumns>;
using grid = std::array<gridRow, MaxRows>;
grid CreateGrid(const std::string &keyword)
{
grid g;
std::array<bool, 256> lookup{};
size_t numFilled = 0;
for(size_t i = 0; (i < keyword.size()) && (numFilled < MaxCells); ++i)
{
if (!lookup[static_cast<unsigned char>(keyword[i])])
{
lookup[static_cast<unsigned char>(keyword[i])] = true;
g[numFilled / 3][numFilled % 3] = keyword[i];
++numFilled;
}
}
for(char ch = 'A'; (ch <= 'Y') && (numFilled < MaxCells); ++ch)
{
if (!lookup[static_cast<unsigned char>(ch)])
{
g[numFilled / 3][numFilled % 3] = ch;
++numFilled;
}
}
return g;
}
int main()
{
grid myArray = CreateGrid("HELO");
...
return 0;
}
Live Demo
Alternatively, you can use a std::set or std::unordered_set for the lookup:
#include <unordered_set>
const size_t MaxRows = 3;
const size_t MaxColumns = 3;
const size_t MaxCells = MaxRows * MaxColumns;
using gridRow = std::array<char, MaxColumns>;
using grid = std::array<gridRow, MaxRows>;
grid CreateGrid(const std::string &keyword)
{
grid g;
std::unordered_set<char> lookup;
size_t numFilled = 0;
for(size_t i = 0; (i < keyword.size()) && (numFilled < MaxCells); ++i)
{
if (lookup.insert(keyword[i]).second)
{
g[numFilled / 3][numFilled % 3] = keyword[i];
++numFilled;
}
}
for(char ch = 'A'; (ch <= 'Y') && (numFilled < MaxCells); ++ch)
{
if (lookup.insert(ch).second)
{
g[numFilled / 3][numFilled % 3] = ch;
++numFilled;
}
}
return g;
}
int main()
{
grid myArray = CreateGrid("HELO");
...
return 0;
}
Live Demo

Related

c++ Split char array without use of any library

I've been running into this weird issue where the split code returns correctly when I printf output inside the function, but will incorrectly return output upon calling it as an instance.
Question: How do I get the correct ouput when calling it as an instance?(see useage bellow)
Here is the code:
typedef struct SplitText
{
int splitLen;
char* splitTxt[100];
char* subTxt(char* text, int index, int len)
{
char subTxt_[1000];
int count = 0;
for (int i = 0; i < 1000; i++)
subTxt_[i] = '\0';
for (int i = index; i < index + len; i++)
subTxt_[count++] = text[i];
return subTxt_;
}
void split(char* text, char sep)
{
char separator[3] = { '<', sep, '>' };
int textLen = strlen(text);
int splitIndex = 0;
int splitCount = 0;
for (int t = 0; t < textLen; t++)
{
if (text[t] == separator[0] && text[t + 1] == separator[1] && text[t + 2] == separator[2])
{
if (splitIndex != 0)
splitIndex += 3;
splitTxt[splitCount] = subTxt(text, splitIndex, t - splitIndex);
splitIndex = t;
//correct output
printf(splitTxt[splitCount]);
printf("\n");
splitCount++;
}
}
splitLen = splitCount;
}
}SplitText;
Useage:
SplitText st;
st.split("testing<=>split<=>function<=>", '=');
for (int i = 0; i < st.splitLen; i++)
{
//incorrect output
printf(st.splitTxt[i]);
printf("\n");
}
printf("--------\n");
This:
char* subTxt(char* text, int index, int len)
{
char subTxt_[1000];
...
return subTxt_;
}
Is undefined behavior. Returning a pointer to a local stack variable (or local array var) is going to result in weird stuff like this happening.
The typical thing that corrupts the contens of that returned pointer is when another function is invoked, the memory occupied by subTxt_ is going to get overwritten with the stack variables of the next function invoked.
Better:
char* subTxt(char* text, int index, int len)
{
char *subTxt = new char[1000];
...
return subTxt_;
}
And then make sure whoever invokes subTxt remembers to delete [] on the returned pointer.
Or just use std::string and be done with it (unless this is an academic exercise).
Also, this is undefined behavior:
for (int t = 0; t < textLen; t++)
{
if (text[t] == separator[0] && text[t + 1] == separator[1] && text[t + 2] == separator[2])
when t == textLen-1, then referencing text[t+2] and text[t+1] is an out of bounds access. Change it to be:
for (int t = 2; t < textLen; t++)
{
if (text[t-2] == separator[0] && text[t -1] == separator[1] && text[t] == separator[2])
And do similar fixups with t within the block as well.
Well you can create a splitstring function instead of a struct/class.
Anyway your code still looks quite "C" like with its fixed size char arrays. This will limit the usability and stability (out-of-bound array bugs).
Strings in C++ are usually of type std::string.
and then C++ has string_view to make views on that string (so no data gets copied, but it also means your string_view is only valid for as long as the string it is viewing lives).
If you don't know the number of substrings in a string up-front, you should not use a fixed size array, but a std::vector (which can resize internally if needed)
This is what a split_string function would look like in current C++, note that the code also shows better what it is doing compared to "C" style programming that show more what you are doing.
std::vector<std::string_view> split_string(std::string_view string, std::string_view delimiters)
{
std::vector<std::string_view> substrings;
if(delimiters.size() == 0ul)
{
substrings.emplace_back(string);
return substrings;
}
auto start_pos = string.find_first_not_of(delimiters);
auto end_pos = start_pos;
auto max_length = string.length();
while(start_pos < max_length)
{
end_pos = std::min(max_length, string.find_first_of(delimiters, start_pos));
if(end_pos != start_pos)
{
substrings.emplace_back(&string[start_pos], end_pos - start_pos);
start_pos = string.find_first_not_of(delimiters, end_pos);
}
}
return substrings;
}
Take a look at std::string_view.
You can avoid allocating memory and it has a built-in substring function.
Just be careful when using printf for printing to console as "%s" will
print the whole string.
See printf documentation.
for(auto view : container_with_string_views)
printf("%.*s, (int)view.size(), view.data());

alternate way for setting array size inside function which was passed as parametre into a function

This is a member function:
Circle returnlargetcircle(Circle obj[], int size)
{
int radi[size];
for (int i = 0; i < size; i++)
{
radi[i] = obj[i].getradius();
}
for (int i = 0; i < size; i++)
{
if (radi[0] < radi[i])
{
radi[0] = radi[i];
}
}
}
expression must have a constant value --the value of parameter "size "(declared in line 61) can not be used as constant
What should be done in this case. I can't do this since my compiler is not allowing me to do this. What is an alternate way for this?
Array sizes must be compile time constants. You can use std::vector for dynamically sized arrays.
However, you don't need an array in the first place. Use std::max_element with a custom comparator, and don't forget to return a circle:
Circle returnlargetcircle(Circle obj[], int size) {
return *std::max_element(obj,obj+size,
[](const Circle& a, const Circle& b) {
return a.getradius() < b.getreadius();
});
}
You should also handle the case when obj is empty. You cannot return a Circle when there is none.
And if this is for an exercise and you are not allowed to use any std:: stuff then you still do not need the extra array:
Circle returnlargetcircle(Circle obj[],int size)
{
int max_radius = obj[0];
size_t max_index = 0;
for (size_t i = 1; i < size; i++) {
if (obj[i].getradius() > max_radius) {
max_radius = obj[i].getradius();
max_index = i;
}
}
return obj[i];
}
(again this assumes that obj has at least one element)

deleting duplicates from a sorted vector

26. Remove Duplicates from Sorted Array
Given a sorted array nums, remove the duplicates in-place such that
each element appear only once and return the new length.
Do not allocate extra space for another array, you must do this by
modifying the input array in-place with O(1) extra memory.
Example 1:
Given nums = [1,1,2],
Your function should return length = 2, with the first two elements of
nums being 1 and 2 respectively.
It doesn't matter what you leave beyond the returned length. Example
2:
Given `nums = [0,0,1,1,1,2,2,3,3,4],
Your function should return length = 5, with the first five elements
of nums being modified to 0, 1, 2, 3, and 4 respectively.
It doesn't matter what values are set beyond the returned length.
Clarification:
Confused why the returned value is an integer but your answer is an
array?
Note that the input array is passed in by reference, which means
modification to the input array will be known to the caller as well.
Internally you can think of this:
// nums is passed in by
reference. (i.e., without making a copy) int len =
removeDuplicates(nums);
// any modification to nums in your function would be known by the
caller. // using the length returned by your function, it prints the
first len elements. for (int i = 0; i < len; i++) {
print(nums[i]); }
i am getting this runtime error while submitting it on leetcode it works fine on coding blocks but shows this error in leetcode compilor
Line 924: Char 9: runtime error: reference binding to null pointer of type 'int' (stl_vector.h)
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/stl_vector.h:933:9
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int k=nums[0];
for(auto i=nums.begin()+1;i<nums.end();i++)
{
if(*i==k) nums.erase(i) , i--;
else k=*i;
}
return nums.size();
}
};
Can anybody help me in finding the cause of error?
Your code works just fine, missing one edge case (an empty nums):
if (nums.empty()) {
return 0;
}
Updated Code:
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
if (nums.empty()) {
return 0;
}
int k = nums[0];
for (auto i = nums.begin() + 1; i < nums.end(); i++) {
if (*i == k) {
nums.erase(i) , i--;
}
else {
k = *i;
}
}
return nums.size();
}
};
Maybe we could just write a simple loop using size(). This'd pass on LeetCode:
// Most of headers are already included;
// Can be removed;
#include <cstdint>
#include <vector>
// Start
static const struct Solution {
using SizeType = std::uint_fast16_t;
static const int removeDuplicates(
std::vector<int>& nums
) {
const SizeType len = std::size(nums);
SizeType count = 0;
for (SizeType i = 1; i < len; ++i) {
if (nums[i] == nums[i - 1]) {
++count;
} else {
nums[i - count] = nums[i];
}
}
return len - count;
}
};

Error using dynamic 2D array

so recently I have been working on a truth table generator which generates an equation for a logic statement. So, I decided to use a 2D array to make things seem more clean and neat. However, as I run this code(Don't worry, int main and other functions exist too, I am posting only this part since it is the one that causes an error):
int calculate(size_t length) {
int dimensionX = length + 2;
int dimensionY = 5;
calcArray = new string *[dimensionX];
for (int i = 0; i < dimensionX; i++) {
calcArray[i] = new string[dimensionY];
}
for (int i = 0; i < dimensionX; i++) {
delete[] calcArray[i];
delete[] calcArray;
}
return 0;
}
int count(string phrase, string calcArray[][]) {
string statement(phrase);
size_t ve = count(statement.begin(), statement.end(), 'A');
size_t yada = count(statement.begin(), statement.end(), 'V');
size_t ozel_yada = count(statement.begin(), statement.end(), 'XV');
size_t ok = count(statement.begin(), statement.end(), '->');
size_t cift_ok = count(statement.begin(), statement.end(), '<->');
size_t complete = ve + yada + ozel_yada + ok + cift_ok;
size_t column_number = complete;
return complete;
for (int i = 3; i < complete; i++) {
int andNum = phrase.find('A');
if (count (phrase.substr(andNum-3,6).begin(), phrase.substr(andNum - 3, 6).end(),'(') == 0){
string calcArray[0][i] = phrase.substr(andNum - 1, 4);
}
}
}
I get errors saying that:
An array may not have elements of this type( int count(string phrase, string calcArray[][])
cannot allocate an array of constant size 0 (string calcArray[0][i] = phrase.substr(andNum - 1, 4);)
An expression did not evaluate a constant (string calcArray[0][i] = phrase.substr(andNum - 1, 4);)
I would appreciate any help or explanation. Thanks
string calcArray[][]
is not a valid declaration for a function parameter. When you use a multi-dimensional array, all dimensions except the first one must be specified. E.g.
string calcArray[][10]
string anotherArray[][10][2]
If the arrays are dynamic, you can use std::vector<std::vector<std::string>>.
std::vector<std::vector<std::string>>& calcArray

c++ shift array elements to left

I've only recently taken up C++ and am having difficulty shifting array elements to remove empty/null elements
char *aBlock;
aBlock = new char[100];
int main(int argc, char **argv)
{
aBlock[20] = 'a'; // fill array with test data.
aBlock[10] = 's';
aBlock[30] = 'd'; // Excepted output: This test data should be shifted to the start of array
// Consider aBlock contains data, with random empty elements
for(int i=1; i <= aBlock.length(); i++) {
if(aBlock[i-1] == 0) {
aBlock[i-1] = aBlock[i];
aBlock[i] = 0;
}
}
return 0;
}
Edit:
Fixed a code typo & wrong variable names, changed "==" to "=".
It still doesn't work as expected.
If I understood correctly, you want to move the non-zero elements at the beginning of your array. You could use std::remove_if to do this and set the rest of the elements to 0.
std::fill(
std::remove_if(std::begin(aBlock), std::end(aBlock), [](char const c) {return c == '\0'; }),
std::end(aBlock),
0);
UPDATE:
Since the array is dynamically allocated you need a small change:
std::fill(
std::remove_if(&aBlock[0], &aBlock[100], [](char const c) {return c == '\0'; }),
&aBlock[100],
0);
Operator == checks the equality, you must use = to assign.
memBlock[i-1] = memBlock[i];
Arrays in C++ have not any member like .length(). They are not classes.
for(int i=1; i <= 100; i++)
^^^
If you know the size at compile-time, use std::array if available. Then you can do ".size()" :) Also, your code doesn't work if you have several consecutive zeroes. Every element is shifted to the left at most once, which is clearly insufficient to achieve your desired result. What you need to do is keep track of a separate "output" index/iterator which receives any non-zero value you encounter and is then incremented:
std::array<char, 100> aBlock;
aBlock[10] = 's';
aBlock[20] = 'a';
aBlock[30] = 'd';
auto output = aBlock.begin(); // or aBlock if you don't have std::array;
for (auto input = aBlock.begin(); input != aBlock.end(); ++input)
{
if (*input != 0)
{
if (output != input)
{
*output = input;
*input = 0;
}
++output;
}
}
That should do the trick.
int arrsize = 100;
...
int i, j;
for(i = 0, j = 0; i < arrsize ; i++) {
if(memBlock[i] != 0 && i != j) {
memBlock[j++] = memBlock[i];
memBlock[i] = 0;
}
}
Side note: new in global space? And where is delete[] ?
change
memBlock[i-1] == memBlock[i];
to
memBlock[i-1] = memBlock[i];
"==" is the problem i think.
use
if(aBlock[i-1] != 0)