How to sort std::pair first only? - c++

I have task to make method that:
get numbers from string
every number is equal to its summed sub-numbers
now sort these sums
display the sorted sums and their original values
I made that function:
void orderWeight(const std::string &strng)
{
std::vector<pair<int, int>> result;
int r{}, rr{}, rrr{};
std::stringstream ss(strng);
while(ss>>r){
rrr = r;
while(r!=0){
rr += r%10;
r /= 10;
}
result.push_back( make_pair(rr, rrr));
rr = 0;
r = 0;
}
std::sort( result.begin(), result.end());
for( int i=0; i<result.size();i++){
cout<<result[i].first<<" "<<result[i].second<<endl;
}
}
This function for arguments - 56 65 74 100 99 68 86 180 90 returns :
1 100
9 90
9 180
11 56
11 65
11 74
14 68
14 86
18 99
But I don't want to sort it by first value THEN by second as You can see in
9 90
9 180
If two numbers are the same, don't sort them by second value, just how are they in a string.
How can I make it happen?

You can make it happen by providing a predicate to sort that tells it how to sort. For example, a simple lambda:
std::sort( result.begin(), result.end(),
[](const auto& lhs, const auto& rhs) {
return lhs.first < rhs.first;
});
The above will sort only by the first value.
See also: https://en.cppreference.com/w/cpp/algorithm/sort

Related

Sorting the first column of a 2D Array in alphabetical order without the sort function [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 2 days ago.
Improve this question
I need to sort the first column of my 2D array into alphabetical order. I am not allowed to use the algorithm library so I cannot use the sort function... additionally, I need to move the rest of the row along with the words being sorted.
I tried iterating through the first column of my matrix and comparing one element to its proceeding element and swapping the two values via temp variable. When I do this, it messes up the 2d array entirely.
void sortTable(){
string temp;
for(int i = 0; i < noRows; i++){
for(int j = i+1; j < noRows; j++){
if(myTable[i][0] < myTable[j][0]){
temp = myTable[i][0];
myTable[i][0] = myTable[j][0];
myTable[j][0] = temp;
}
}
}
}
this is the table:
Dave Philadelphia M 39 72 167.6
Carl Izmir M 32 70 155.9
Alex Singapore M 41 74 170.5
Bert Zhongshan M 42 68 166.8
Luke Porto Alegre M 34 72 163.6
Myra Karaj F 23 62 98.8
Elly Vienna F 30 66 124.9
Jake Ulsan M 32 69 143.5
Fran Hamburg F 33 66 115.5
Omar Kampala M 38 70 145.4
Page Tehran F 31 67 135.2
Quin Chennai M 29 71 176.0
Hank Shanghai M 30 71 158.7
Ivan London M 53 72 175.9
Kate Patna F 47 69 139.3
Neil Daejeon M 36 75 160.9
Ruth Managua F 28 65 131.8
Gwen Bucharest F 26 64 121.1
which needs to end up as...
Alex Singapore M 41 74 170.5
Bert Zhongshan M 42 68 166.8
Carl Izmir M 32 70 155.9
Dave Philadelphia M 39 72 167.6
Elly Vienna F 30 66 124.9
Fran Hamburg F 33 66 115.5
Gwen Bucharest F 26 64 121.1
Hank Shanghai M 30 71 158.7
Ivan London M 53 72 175.9
Jake Ulsan M 32 69 143.5
etc..
As indicated in my comments, you can sort the array without sorting the array. The trick is to use an index array, and instead the index array is sorted.
Something similar to this:
std::vector<int> idx;
//...
void sortTable(int noRows)
{
idx.resize(noRows);
// Initialize the index array
for (int i = 0; i < noRows; ++i)
idx[i] = i;
// Sort using the index array
for(int i = 0; i < noRows; i++)
{
for(int j = i+1; j < noRows; j++)
{
// Note that we use the index array idx, and not simply i and j
if(myTable[idx[i]][0] > myTable[idx[j]][0])
{
// Note we are swapping the index, and not the rows themselves
int temp = idx[i];
idx[i] = idx[j];
idx[j] = temp;
}
}
}
Then when it comes time to display the results:
for (int i = 0; i < nRows; ++i)
{
for (int j = 0; j < 5; ++j)
std::cout << myTable[idx[i]][j] << " ";
std::cout << "\n";
}
Live Example
If you can't use std::vector, then create an array that has enough elements, and initialize the array with elements 0, 1, 2, etc. up to nRows-1.

Does view::join require copyable inner range? Why?

Suppose that we have
cppcoro::generator<int> gen_impl(int in) {
const auto upper = in + 10;
for (; in < upper; ++in)
co_yield in;
}
cppcoro::generator<cppcoro::generator<int>> gen() {
for (int n = 1; n < 100; n += 10)
co_yield gen_impl(n);
}
So we can iterate inner range just fine
for (auto&& row : gen() ) {
for (auto n : row)
std::cout << n << ' ';
std::cout << '\n';
}
NOTE: range-for on ref is required because cppcoro::generator doesn't allow copying (deleted copy ctor)
Print
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48 49 50
51 52 53 54 55 56 57 58 59 60
61 62 63 64 65 66 67 68 69 70
71 72 73 74 75 76 77 78 79 80
81 82 83 84 85 86 87 88 89 90
91 92 93 94 95 96 97 98 99 100
But when we try to "flattern" with view::join
auto rng = gen();
for (auto n : rng | ranges::view::join) {
std::cout << n << '\n';
};
It seems view::join require Copyable inner range?
In file included from <source>:3:
In file included from /opt/compiler-explorer/libs/rangesv3/trunk/include/range/v3/view.hpp:38:
In file included from /opt/compiler-explorer/libs/rangesv3/trunk/include/range/v3/view/for_each.hpp:23:
/opt/compiler-explorer/libs/rangesv3/trunk/include/range/v3/view/join.hpp:320:50: error: call to deleted constructor of 'cppcoro::generator<cppcoro::generator<int> >'
return join_view<all_t<Rng>>{all(static_cast<Rng&&>(rng))};
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/libs/rangesv3/trunk/include/range/v3/view/view.hpp:112:21: note: in instantiation of function template specialization 'ranges::v3::view::join_fn::operator()<cppcoro::generator<cppcoro::generator<int> > &, false, nullptr>' requested here
v.view_(static_cast<Rng&&>(rng))
^
/opt/compiler-explorer/libs/rangesv3/trunk/include/range/v3/utility/functional.hpp:731:42: note: in instantiation of function template specialization 'ranges::v3::view::view<ranges::v3::view::join_fn>::pipe<cppcoro::generator<cppcoro::generator<int> > &, ranges::v3::view::view<ranges::v3::view::join_fn> &, false, nullptr>' requested here
pipeable_access::impl<Pipe>::pipe(static_cast<Arg&&>(arg), pipe)
^
<source>:35:21: note: in instantiation of function template specialization 'ranges::v3::operator|<cppcoro::generator<cppcoro::generator<int> > &, ranges::v3::view::view<ranges::v3::view::join_fn>, false, nullptr>' requested here
for (auto n : rng | ranges::view::join) {
^
/opt/compiler-explorer/libs/cppcoro/include/cppcoro/generator.hpp:174:3: note: 'generator' has been explicitly marked deleted here
generator(const generator& other) = delete;
^
/opt/compiler-explorer/libs/rangesv3/trunk/include/range/v3/view/join.hpp:76:36: note: passing argument to parameter 'rng' here
explicit join_view(Rng rng)
^
What makes this not compiled?
Is there any bug in range-v3 or cppcoro?
Only incompatible design decisions?
godbolt (Full)
In range-v3, a move-only view is OK. That got implemented late and there may still be bugs, but that's not what is happening here.
The first problem is that you are trying to adapt an lvalue of type cppcoro::generator here:
auto rng = gen();
for (auto n : rng | ranges::view::join) {
Since a generator is a view, the join view will want to copy it. It can't because it is not copyable.
You can fix this problem by moving the generator in:
auto rng = gen();
for (auto n : std::move(rng) | ranges::view::join) {
Then you run into the next problem, which is that the reference type of generator<generator<int>> is const generator<int>&, and you have the same problem again: the join wants to hold a copy of the inner generator while it iterates over it, but it cannot make a copy.
The workaround is a bit ugly: change the generator to return a non-const lvalue reference:
cppcoro::generator<cppcoro::generator<int>&> gen() {
for (int n = 1; n < 100; n += 10) {
auto tmp = gen_impl(n);
co_yield tmp;
}
}
and then std::move each inner range with a move view:
auto rng = gen();
for (auto n : std::move(rng) | ranges::view::move | ranges::view::join) {
std::cout << n << '\n';
}
The result compiles. Whether it runs or not depends on how gracefully cppcoro handles the case where someone steals away the guts of the value that it safely tucked away in the coroutine's promise type.
https://godbolt.org/z/mszidX
A note about the future std::view::join:
The join view that will ship with C++20 is a little different. If the outer range's reference type is a real reference (as in this case), it will not try to make a copy of the view to which it refers. That means in C++20, you won't need the ugly view::move hack.
However, the C++20 View concept currently requires copyability so this solution still won't work. We have a TODO item to relax this before C++20 ships, but there's no telling how the Committee will like that idea.

How do check an array for repeat numbers in c++? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I'm trying to produce an array with 4 random generated [srand seeded rand()] without any repeats. I'm using a for loop to:
Select a position in the array,
Generate a number,
Assign the number to the currently highlighted position
Check that the assigned number is not equal to a previous entry, as per the following pseudocode.
if no -
Then select the next position in the array and generate a new number
if yes -
Do not move to the next array position and generate a new number again.
repeat until array position 3
This was my attempt:
int operator_selection;
int operator_index[3];
int random_value;
for (operator_selection = 0; operator_selection < 4; operator_selection++)
{
random_value = rand() %4 + 1;
if (random_value = operator_index[0] || operator_index[1] || operator_index[2])
{
(operator_selection - 1);
}
operator_index[operator_selection] = random_value;
cout<<operator_index[operator_selection]<<" ";
if (operator_selection == 3)
{
cout<<endl;
}
}
However when I run the executable I always end up with repeats, so I'm pretty sure the logic behind my first 'if statement' is flawed.
I'm a c++ beginner and this is my 3rd attempt at writing a source file from scratch, so apologies if I've made a silly mistake.
I see several problems in your posted code.
Problem 1
The line
if (random_value = operator_index[0] || operator_index[1] || operator_index[2])
does not do what you are hoping to do. You need to use:
if ( (random_value == operator_index[0]) ||
(random_value == operator_index[1]) ||
(random_value == operator_index[2]) )
Problem 2
Comparing random_value against operator_index[0] and operator_index[1] and operator_index[2] is incorrect. You only need to compare up to operator_index[operator_selection-1].
Problem 3
The line
(operator_selection - 1);
does not change the value of operator_selection. It just evaluates the expression and discards the value.
What you need is a statement that decrements the value of operator_selection. E.g.
--operator_selection;
Problem 4
You need to continue to the next iteration of the loop when you find an existing value.
Here's an updated version of the loop:
for (operator_selection = 0; operator_selection < 4; operator_selection++)
{
random_value = rand() %4 + 1;
bool matchFound = false;
for ( int i = 0; i < operator_selection-1; ++i )
{
if ( random_value == operator_index[i] )
{
matchFound = true;
break;
}
}
if ( matchFound )
{
--operator_selection;
continue;
}
operator_index[operator_selection] = random_value;
cout<<operator_index[operator_selection]<<" ";
}
// Move this out of the loop.
cout<<endl;
Here is a version using std::array and std::random_shuffle:
#include <iostream>
#include <array>
#include <algorithm>
#include <random>
int main()
{
std::array<int, 4> a = {1, 2, 3, 4};
std::random_device rd;
std::mt19937 g(rd());
std::shuffle(a.begin(), a.end(), g);
for(auto& i : a)
std::cout << i << " ";
}
Live Demo
This version is more readable and more efficient.
Update: this does not answer the question as it is and does not fit if it's a home-work. But I would leave it here just in the case OP is interested in a better alternative.
First, you need to make your array bigger. As already mentioned, it would be nicer to use a std::array, but I'll stick with the old style one. You define operator_index with a dimension of 3 which only allows for 3 elements (with offsets from 0 to 2). So this needs a dimension of 4.
You should also initialize the contents of the array (or ensure that you never read from uninitialized elements).
Next, for the first random number there is no possibility of a collision. So you can put it directly into the array.
operator_index[0] = rand() %4 + 1;
You can then loop from 1 to 3 for the remaining 3 entries.
You can even go a little further than this. When you have filled the first 3 entries you can directly calculate the last,
operator_index[3] = 10 - operator_index[2] - operator_index[1] - operator_index[0];
(the sum from 1 to 4 is 10, so the last element is 10 - the sum of the first three)
The main problem with your code is this
if (random_value = operator_index[0] || operator_index[1] || operator_index[2])
{
(operator_selection - 1);
}
This does an assignment, not an equality check. It is assigned the logical OR of the first 3 elements. Since you do not initialize the array you will be reading garbage, and the result is probably going to be that random_value will be set to one and the condition will evaluate to true.
(operator_selection - 1) is an operator without side effects. It does not modify operator_selection. Also once you have found a duplicate, you want to start your loop again.
Here's a version that minimizes the looping.
#include <iostream>
#include <cstdlib>
#include <ctime>
int main()
{
int operator_selection;
int operator_index[4] = {0};
int random_value;
srand(time(0));
operator_index[0] = rand() %4 + 1;
for (operator_selection = 1; operator_selection < 3; operator_selection++)
{
random_value = rand() %4 + 1;
if (operator_index[0] == random_value || operator_index[1] == random_value)
{
--operator_selection;
continue;
}
operator_index[operator_selection] = random_value;
}
operator_index[3] = 10 - operator_index[2] - operator_index[1] - operator_index[0];
for(auto& elem : operator_index)
std::cout << elem << " ";
std::cout << "\n";
}
All that said, I still prefer the std::random_shuffle approach, which I would also suggest.
Another trick to is to reverse the lopp one step back if the conditions are not met.
#include <iostream>
#include <ctime>
using namespace std;
int main(void)
{
const int size=100 ;
int arr[100] ;
int i=0;
srand(time(0));
for ( i=0;i<size;i++) {
arr[i]=rand() % size;
for(int j=0; j < i ; j++) if (arr[j] == arr[i]) i--;
}
cout<<" \n\n\n ";
// Loop to display the array arr[ ]
for ( i=0;i<size;i++) cout<<""<<arr[i]<<"\t";
cout<<" \nPress any key to continue\n";
cin.ignore();
cin.get();
return 0;
}
output:
91 71 14 65 12 25 64 98 83 28
99 9 5 0 89 36 95 55 73 90
78 2 52 70 39 63 17 50 7 58
34 84 40 51 20 31 38 32 35 49
61 66 72 92 6 59 41 13 22 23
81 56 1 16 21 62 57 10 11 54
77 86 76 93 4 96 8 33 94 67
29 48 15 82 97 37 26 46 43 80
68 85 60 30 42 53 18 69 45 88
47 79 75 44 24 27 74 3 19 87
Press any key to continue

c++ class return reference to itself

Can a c++ object have a method that return a reference to itself?
I want multiple independent instance of the class of the object.
It is for implementing the [] operator of a multi-dimensional array.
I want something like Array[0][1][2] to work.
Thanks.
Update:
Implementation of a multi-dimensional array:
Intended usage:
A[0][1] = 4; //the element at 0th row and 1st column is now 4.
A[0] = 5; //all elements of the 0th row are now 5.
A["all"][1] = 10; //all elements of the 1st column are now 10;
Object A has pointer ptr_buffer to a chunk of memory with correct simd alignment. Constructor of A would allocate the memory. Destructor of A deallocate the memory. A[] returns object B.
Object B has a pointer ptr_A_buffer to a subsection of the memory of A. B[] modifies ptr_A_buffer and also returns reference to itself. I don't want to constantly make a bunch of object B for each [] operation.
Both A and B belong to the same abstract class. Math functions take the abstract class as argument.
I was curious about the idea. Here is a data type which seems to fit the bill. It's an array type which, when called with three integers for the dimensions, allocates contiguous memory and then allows the user to define "views" into the data which have smaller dimensions (a plane, a row, a single value).
I made use of shared pointers which I have not really used before, so I may have made mistakes. I welcome corrections.
The idea is that copying views around is shallow; they all operate on the same underlying data. This allows me to pass them around by value with, as I believe, reasonable efficiency.
#include <iostream>
#include <iomanip>
#include <memory>
using namespace std;
/// This class is a three-dimensional array of doubles.
/// It defines an index operator which returns a view into that
/// data that is of one lesser dimension, just like the standard
/// index operator on plain old arrays. The last index operation
/// yields an "array" which is a single value.
/// Converting to double and assigning from double is defined as
/// using the first double in the view.
class NDimArrT
{
/// All slices hold a shared pointer to the base data they
/// are looking into so that their view stays valid.
const shared_ptr<double> baseData;
/// The data the view is looking at. Identical to the shared
/// ptr for the original object.
double *const slice;
/// The three dimensions, in the order of indexing.
/// All three of them may be zero, indicating a single value.
const int dim1, dim2, dim3;
public:
/// A single double value view into a one-dimensional array
NDimArrT(const shared_ptr<double> base, double *sliceStart)
: baseData(base), slice(sliceStart), dim1(0), dim2(0), dim3(0) {}
/// A 1D vector/row view into a two-dimensional array.
/// #param dim1Arg is the length of the row.
NDimArrT(const shared_ptr<double> base, double *sliceStart, int dim1Arg)
: baseData(base), slice(sliceStart), dim1(dim1Arg), dim2(0), dim3(0) {}
/// A 2D matrix plane view into the cube
NDimArrT(const shared_ptr<double> base, double *sliceStart, int dim1Arg, int dim2Arg)
: baseData(base), slice(sliceStart), dim1(dim1Arg), dim2(dim2Arg), dim3(0) {}
/// A 3D cube. This actually allocates memory.
NDimArrT(int dim1Arg, int dim2Arg, int dim3Arg)
: baseData(new double[dim1Arg*dim2Arg*dim3Arg], std::default_delete<double[]>() ),
slice(baseData.get()), // the data view is the whole array
dim1(dim1Arg), dim2(dim2Arg), dim3(dim3Arg) {}
/// Perform a shallow copy. We assume that e.g. returning a slice means
/// essentially returning another view into the main base array.
NDimArrT(const NDimArrT &rhs) = default;
/// Use standard move semantics. The rhs will be invalidated, and the
/// reference count to the base array does not grow. Can be used to return results from
/// functions.
NDimArrT(NDimArrT &&rhs) = default;
/// Default destructor; destroy baseData if it's the last reference.
~NDimArrT() = default;
/// Get the value of the first element we look at. Handy for
/// single value views.
operator double() const { return *slice; }
/// Return an instance of NDimArrT representing a view
/// with one dimension less than this. If we have a single value
/// already, simply return this single value. (We should
/// perhaps throw an exception there.)
NDimArrT operator[](int ind)
{
// This could be regarded an error, because this view
// is already a single element.
if(GetDims() == 0) { return *this; }
// This view is a 1-dimensional vector. Return the element at ind.
if(dim2==0) { return NDimArrT(baseData, slice + ind); } // return a single value.
// This view is a 2D matrix. Return the row no. ind.
// The row length is dim2. (Dim 1 indicates how many rows.)
if(dim3==0) { return NDimArrT(baseData, slice + dim2*ind, dim2); } // return a 1D vector
// This view is a true 3D cube matrix. dim1 is the number of planes,
// dim2 is the number of rows in a plane, dim3 is the length of each row.
// Return the plane no. ind, which starts at ind*planesize, i.e. ind*dim2*dim3.
// The number of rows and row length are dim2 and dim3, respectively.
return NDimArrT(baseData, slice + dim2*dim3*ind, dim2, dim3); // return a 2D matrix.
}
NDimArrT &operator=(double d) { *slice = d; }
int Len() { return dim1 ? dim1 : 1; } // interestingly, length is always dim1.
int GetDims() const
{
return dim1
? dim2
? dim3
? 3
: 2
: 1
: 0;
}
};
/// An example function which initializes an NDimArr of unknown
/// dimensionality with nice numbers..
void initNDimArr(NDimArrT arr, int &startVal)
{
// Single value? Give it the start value and increment that.
if( arr.GetDims() == 0 ) { arr = startVal++; }
else
{
for(int ind=0; ind<arr.Len(); ind++) { initNDimArr(arr[ind], startVal); }
}
}
// An example function doing something with
// an unknown n-dimensional array
void printNdimArr( NDimArrT nDimArr)
{
if( nDimArr.GetDims() == 0) { cout << setw(4) << nDimArr << " "; }
else
{
for(int i=0; i<nDimArr.Len(); i++) { printNdimArr(nDimArr[i]); }
cout << endl;
}
}
int main()
{
NDimArrT arr(3,4,5);
int start = 1;
initNDimArr(arr, start);
printNdimArr(arr);
// now use the middle plane of the 3 4x5 planes
cout << "Middle plane, values starting at 100:" << endl;
auto middlePlane = arr[1];
start = 100;
initNDimArr(middlePlane, start);
printNdimArr(middlePlane);
cout << "Whole array now:" << endl;
printNdimArr(arr);
cout << "Print line 2 of the 3rd plane:" << endl;
printNdimArr(arr[2][1]);
cout << endl << "Last number in that row is " << arr[2][1][4] << endl;
}
Sample session:
$>g++ -std=c++11 -o ndimContiguousArr ndimContiguousArr.cpp && ./ndimContiguousArr
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
21 22 23 24 25
26 27 28 29 30
31 32 33 34 35
36 37 38 39 40
41 42 43 44 45
46 47 48 49 50
51 52 53 54 55
56 57 58 59 60
Middle plane, values starting at 100:
100 101 102 103 104
105 106 107 108 109
110 111 112 113 114
115 116 117 118 119
Whole array now:
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
100 101 102 103 104
105 106 107 108 109
110 111 112 113 114
115 116 117 118 119
41 42 43 44 45
46 47 48 49 50
51 52 53 54 55
56 57 58 59 60
Print line 2 of the 3rd plane:
46 47 48 49 50
Last number in that row is 50

How do I read one number at a time and store it in an array, skipping duplicates?

I'm trying to read numbers from a file into an array, discarding duplicates. For instance, say the following numbers are in a file:
41 254 14 145 244 220 254 34 135 14 34 25
Though the number 34 occurs twice in the file, I would only like to store it once in the array. How would I do this?
(fixed, but I guess a better term would be a 64 bit Unsigned int) (was using numbers above 255)
vector<int64_t> v;
copy(istream_iterator<int64_t>(cin), istream_iterator<int64_t>(), back_inserter(v));
set<int64_t> s;
vector<int64_t> ov; ov.reserve(v.size());
for( auto i = v.begin(); i != v.end(); ++i ) {
if ( s.insert(v[i]).second )
ov.push_back(v[i]);
}
// ov contains only unique numbers in the same order as the original input file.