How to shuffle an array in C++? - c++
I have an array:
names[4]={john,david,jack,harry};
and i want to do it randomly shuffle, like:
names[4]={jack,david,john,harry};
I tried to use this but it just shuffled the letters of the first word in the array:
random_shuffle(names->begin(), names->end());
Here is the full code, it reads names from a .txt file and puts in an array:
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
using namespace std;
int main() {
ifstream readName("names.txt");
string names[197];
int i = 0;
for (string line; getline(readName, line); ){
readName >> names[i];
i++;
}
readName.close();
random_shuffle(names->begin(), names->end());
for (int i = 0; i < 197; i++) {
cout << names[i] << endl;
}
return 0;
}
I tried few other things from different people but I couldn't manage to work it.. Anything helps, thank you!
Here's your code with what I felt were the smallest amount of changes. One could argue that I didn't need to change your first for loop as much, but I figure that if you have the prescience to know how many names you're reading, you might as well use the knowledge.
#include <algorithm>
#include <fstream>
#include <iostream>
#include <iterator> // std::begin(), std::end(); required for C-arrays
#include <random> // std::mt19937; needed to feed std::shuffle()
#include <string>
// using namespace std; // BAD PRACTICE
int main() {
constexpr int size = 4; // Give your magic number a name; only need to change
// a single location
std::ifstream readName("names.txt");
if (!readName) { // Always check that you successfully opened the file.
std::cerr << "Error opening file.\n";
return 1;
}
std::string names[size];
// int i = 0;
for (int i = 0; i < size; ++i) { // Retool the loop entirely
std::getline(readName, names[i]);
}
readName.close();
// This is a fragile solution. It's only working because the array is in
// scope
std::shuffle(std::begin(names), std::end(names),
std::mt19937{std::random_device{}()});
for (int i = 0; i < size; i++) {
std::cout << names[i]
<< '\n'; // Don't use std::endl unless you actually need it
}
return 0;
}
This is not ideal code, though. Any change to the size of the input file requires changing the code and re-compiling. The biggest single change was to get rid of std::random_shuffle and use std::shuffle() instead. std::random_shuffle was deprecated with C++14 and removed in C++17. It's bad to use it. std::shuffle() does add the requirement of providing a PRNG, but it's not so bad. It leads to better code if you have a PRNG that needs to randomize many different things in a bigger program. This is because it's good to have a single PRNG and let it live for the length of your program as opposed to constantly building new ones.
And the C-array just makes things a bit clunkier. Enter std::vector.
#include <algorithm>
#include <fstream>
#include <iostream>
#include <iterator>
#include <random> // std::mt19937; needed to feed std::shuffle()
#include <string>
#include <vector>
int main() {
std::ifstream readName("names.txt");
if (!readName) { // Always check that you successfully opened the file.
std::cerr << "Error opening file.\n";
return 1;
}
std::vector<std::string> names;
std::string name;
while (std::getline(readName, name)) { // Retool the loop entirely
names.push_back(name);
}
readName.close();
std::shuffle(std::begin(names), std::end(names),
std::mt19937{std::random_device{}()});
for (const auto& i : names) {
std::cout << i << '\n';
}
return 0;
}
The vector can grow as needed, so you see how much simpler the loop that reads the names becomes. It's also more flexible since you don't have to know ahead of time how many entries to expect. It will "just work." With the call to std::shuffle() I kept the std::begin(names) syntax because many consider this a best practice, but you could have also used names.begin() if you wanted since the vector class provides its own iterators.
Lets have a look at you main question:
I tried to use this but it just shuffled the letters of the first word in the array:
random_shuffle(names->begin(), names->end());
The reason that it only shuffles the first word is because of the types and usage.
So names is an array of strings.
string names[197];
The problem stems from the C world. Were arrays decay into pointers exceedingly easily (just by being used in an expression). So here names-> has decayed into a pointer to the first element of the array. This allows you to use the -> operator which normally only works on pointers. So you are calling the functions begin() and end() on the pointer to the first element in the array. Thus only the first name is being shuffled.
The fix this problem use std::begin() method.
// here std::begin / std::end find the beginning and end
// of the array. So you are shuffling the array.
random_shuffle(std::begin(names), std::end(names));
But I would note that random_shuffle() is outdated. As mentioned by #sweenish you should use std::shuffle() See his answer for details.
A couple of things we can improve:
You use C array to store the names. Sure it works, but it is susceptible to a couple of issues because it can not be re-sized (and unless you think the file is never going to be changed that may be an issue). Potentially a hidden issue to a far distant maintainer.
std::vector<std::string> names; // resizeable container.
I would note that the current implementation ignores the first line. Then reads the first word from each subsequent line. There is also a slight issue that the last line may be empty and you read an empty name into the last element of the array (but you don't track how many names you read so unless you use all elements in the array you may never notice that).
I would change that. As it is not obvious. I would delibrately and seprately ignore the first line. Then I would simply read all the first words into a vector (so you know the size).
std::string line
std::getline(file, line); // Ignore the first line.
std::string word
while(file >> word) {
names.push_back(word);
std::getline(file, line); // ignore the rest of the line.
}
We could get fancy. Use iterators to simply create the array directly.
class Line
{
std::string firstWord;
friend std::istream& operator>>(std::istream& stream, Line& data) {
stream >> data.firstWord;
stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); retrun stream;
}
operator std::string() const {
return firstWord;
}
};
Now you can create and load the vector in one line:
std::vector<std::string> names(std::istream_iterator<Line>(file),
std::istream_iterator<Line>{});
Then finally copy the names out can be made easier using foreach loop. Also don't use std::endl in a loop like this. It forces a flush of the underlying bugger after each new line. This is very ineffecient.
for(auto const& name: names) {
std::cout << name << "\n";
}
So the result is:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
class Line
{
std::string firstWord;
friend std::istream& operator>>(std::istream& stream, Line& data) {
stream >> data.firstWord;
stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); return stream;
}
operator std::string() const {
return firstWord;
}
};
int main()
{
std::ifstream file("names.txt");
std::string line
std::getline(file, line); // Ignore the first line.
std::vector<std::string> names(std::istream_iterator<Line>(file),
std::istream_iterator<Line>{});
random_shuffle(std::begin(names), std::end(names));
for(auto const& name: names) {
std::cout << name << "\n";
}
}
Related
how to fill a set in C++ with input from user
how can we fill a set in C++ from user input in one liner I know we can store input in a variable and then use set.insert(some_variable) but cant we take input from user directly? like set.insert(cin>>set[i])? I am just a beginner.
You can copy the values into a std::set with std::copy_n. (Please see here) The source will be the std::istream_iterator (described here) and the destination will be the std::inserter wrapper (described here). Please see: #include <iostream> #include <set> #include <iterator> #include <algorithm> int main() { // Our set std::set<int> s{}; // How many values do we want to read const size_t numberOfUserInputs{ 3 }; // Copy values from std::cin to set std::copy_n(std::istream_iterator<int>(std::cin), numberOfUserInputs, std::inserter(s, s.begin())); // Debug output for (int i : s) std::cout << i << '\n'; return 0; }
no, you can't do this. things that go with vector like std::vector<int> he; auto endf = he.end(); *he.insert(endf, 1) = 5; will not go with set. insert returns const_iterator.
is there any way to define dynamic array without Determine the size of it
I need a dynamic array that I don't have to scale(Determine) to a fixed number like the following string* s; I have this code so far, but obviously it doesn't work. #include <iostream> #include <string> #include <fstream> using namespace std; int main() { fstream f; f.open("resa.txt"); string* s; int i = 0; while (f.good()) { f >> *(s + i); i++; } return 0; } This is my task: Now we change the class definitions a bit. No static arrays can occur anymore. The fact that the arrays instead become dynamic means that some class methods need to be modified, and that some / some of the classes need copy constructors and assignment methods (or superimposed assignment operator). [...]" This means, that I just can't use data structures.
It's not automatic, you have to allocate more memory every time you want to resize, copy elements into new array and delete the old one. Fortunately, standard library got you covered with std::vector - an automatically resizable array. #include <iostream> #include <string> #include <fstream> #include <vector> using namespace std; int main() { fstream f; f.open("resa.txt"); string temp; std::vector<std::string> s; while (f >> temp) { s.push_back(temp); } return 0; } I also fixed your input reading - see Why is iostream::eof inside a loop condition (i.e. while (!stream.eof())) considered wrong? (applies to good() as well). Alternatively, you can use std::istream_iterator to initialize vector in one line instead of using loop (credit to Ayxan): vector<string> s{ istream_iterator<string>{f}, {} };
How can i initialize a set using loop in c++?
I need to know can initialize a set using loop and how ? What should i make in this code ? #include <iostream> #include <set> using namespace std; int main() { set <char>s; for (auto it = s.begin(); it != s.end();it++){ cin >>*it; } return 0; }
I'm going to assume you want to loop over all the user input. Note that the simplest way I have shown will ignore whitespace. #include <iostream> #include <set> int main() { std::set<char> s; char c; // input from user while (std::cin >> c) { // read until end of input s.insert(c); } // do something with s, I guess? } This: std:cin >> c will fail when the user finishes their input, which will terminate the loop. I'll repeat myself: std::cin >> skips whitespace. If you want to also read any whitespace characters the user inputs, I can show a way to do that too. Iterators only let you access items that are already in the std::set (or other container), and in the case of std::set you can't assign to things once they are in there. Please also note that using namespace std; is widely considered to be bad practice.
ostream, copy function printing string address, instead of string contents
This prints the address for my string, but not its' contents, #include <memory> #include <string> #include <list> #include <iostream> #include <iterator> using namespace std; int _tmain(int argc, _TCHAR* argv[]) { unique_ptr<list<shared_ptr<string>>> upList (new list<shared_ptr<string>>); shared_ptr<string> spNation (new string ("India")); upList->push_back (spNation); copy (upList->begin(), upList->end(), ostream_iterator<shared_ptr<string>> (cout, "\n ")); return 0; } My questions are: What ostream_iterator<shared_ptr<string>> is taking shared_ptr or strings as its' prime object. How to print actual string contents (i.e. India) using this approach. Is this approach is preferable over traditional for loop to print all node contents.
What ostream_iterator<shared_ptr<string>> is taking shared_ptr or strings as its' prime object. You've instantiated ostream_iterator for shared_ptr<string>, so that is what it will attempt to output. How to print actual string contents (i.e. India) using this approach. If you really want to use shared pointers for some reason, then you can't use copy since that won't undo the extra level of indirection. Either use a plain loop, or get rid of the unnecessary indirection: list<string> list; list.push_back("India"); copy(list.begin(), list.end(), ostream_iterator<string>(cout, "\n ")); Of course, it doesn't look as exciting without all the arrows, templates, new-expressions and pseudohungarian warts, but anyone trying to maintain the code won't thank you for adding such embellishments. Is this approach is preferable over traditional for loop to print all node contents It's preferable when it makes the code simpler. When it doesn't, it isn't.
Firstly: why you use shared_ptr<string> instead of string here? You shouln't do this. 1) shared_ptr<string> 2) Use std::for_each with lambda (or range-based for loop) for_each(upList->begin(), upList->end(), [](const shared_ptr<string>& p) { cout << *p << endl; }); or for (const auto& p : upList) { std::cout << *p << std::endl; }
Can we split, manipulate and rejoin a string in c++ in one statement?
This is a bit of a daft question, but out of curiousity would it be possibly to split a string on comma, perform a function on the string and then rejoin it on comma in one statement with C++? This is what I have so far: string dostuff(const string& a) { return string("Foo"); } int main() { string s("a,b,c,d,e,f"); vector<string> foobar(100); transform(boost::make_token_iterator<string>(s.begin(), s.end(), boost::char_separator<char>(",")), boost::make_token_iterator<string>(s.end(), s.end(), boost::char_separator<char>(",")), foobar.begin(), boost::bind(&dostuff, _1)); string result = boost::algorithm::join(foobar, ","); } So this would result in turning "a,b,c,d,e,f" into "Foo,Foo,Foo,Foo,Foo,Foo" I realise this is OTT, but was just looking to expand my boost wizardry.
First, note that your program writes "Foo,Foo,Foo,Foo,Foo,Foo,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,," to your result string -- as already mentioned in comments, you wanted to use back_inserter there. As for the answer, whenever there's a single value resulting from a range, I look at std::accumulate (since that is the C++ version of fold/reduce) #include <string> #include <iostream> #include <numeric> #include <boost/tokenizer.hpp> #include <boost/algorithm/string.hpp> #include <boost/bind.hpp> std::string dostuff(const std::string& a) { return std::string("Foo"); } int main() { std::string s("a,b,c,d,e,f"); std::string result = accumulate( ++boost::make_token_iterator<std::string>(s.begin(), s.end(), boost::char_separator<char>(",")), boost::make_token_iterator<std::string>(s.end(), s.end(), boost::char_separator<char>(",")), dostuff(*boost::make_token_iterator<std::string>(s.begin(), s.end(), boost::char_separator<char>(","))), boost::bind(std::plus<std::string>(), _1, bind(std::plus<std::string>(), ",", bind(dostuff, _2)))); // or lambda, for slightly better readability std::cout << result << '\n'; } Except now it's way over the top and repeats make_token_iterator twice. I guess boost.range wins.
void dostuff(string& a) { a = "Foo"; } int main() { string s("a,b,c,d,e,f"); vector<string> tmp; s = boost::join( ( boost::for_each( boost::split(tmp, s, boost::is_any_of(",")), dostuff ), tmp ), "," ); return 0; } Unfortunately I can't eliminate mentioning tmp twice. Maybe I'll think of something later.
I am actually working on a library to allow writing code in a more readable fashion than iterators alone... don't know if I'll ever finish the project though, seems dead projects tend to accumulate on my computer... Anyway the main reproach I have here is obviously the use of iterators. I tend to think of iterators as low-level implementation details, when coding you rarely want to use them at all. So, let's assume that we have a proper library: struct DoStuff { std::string operator()(std::string const&); }; int main(int argc, char* argv[]) { std::string const reference = "a,b,c,d,e,f"; std::string const result = boost::join( view::transform( view::split(reference, ","), DoStuff() ), "," ); } The idea of a view is to be a lightwrapper around another container: from the user point of view it behaves like a container (minus the operations that actually modify the container structure) from the implementation point of view, it's a lightweight object, containing as few data as possible --> the value is ephemeral here, and only lives as long as the iterator lives. I already have the transform part working, I am wondering how the split could work (generally), but I think I'll get into it ;)
Okay, I guess it's possible, but please please don't really do this in production code. Much better would be something like std::string MakeCommaEdFoo(std::string input) { std::size_t commas = std::count_if(input.begin(), input.end(), std::bind2nd(std::equal_to<char>(), ',')); std::string output("foo"); output.reserve((commas+1)*4-1); for(std::size_t idx = 1; idx < commas; ++idx) output.append(",foo"); return output; } Not only will it perform better, it will is much easier for the next guy to read and understand.