I am trying to compare the items in a QList.
Here is the old way to do it using QPtrCollection but this cannot be used in versions after qt3 (as far as I'm aware).
class gnyComponentList:public QList<gnyComponent>
{
protected:
virtual int compareItems ( QPtrCollection::Item item1, QPtrCollection::Item item2 )
{ return (((gnyComponent *)item1)->getID()).compare(((gnyComponent *)item2)->getID());}
};
I can't figure out what a good way of doing this in Qt5.3 might be?
You can use the std::equal algorithm on QList objects, as in:
#include <QList>
#include <QString>
#include <algorithm> // for std::equal
struct Person
{
QString firstName;
QString lastName;
};
int main()
{
QList<Person> personsA, personsB;
// Populate personsA and personsB
bool equal = std::equal( personsA.begin(), personsA.end(),
personsB.begin(),
[]( const Person &a, const Person & b ) {
return a.firstName == b.firstName;
} );
}
This is a simple one, which compares every item without sorting.
Here is the code.
bool TeachTab::isTwoStringListEqual(const QStringList &dst,
const QStringList &src) {
if (dst.size() != src.size())
return false;
for (int i = 0; i < dst.size(); ++i) {
if (dst.value(i) != src.value(i)) {
return false;
}
}
return true;
}
Related
This question already has answers here:
vector of class without default constructor
(3 answers)
Object array initialization without default constructor
(14 answers)
C++ compiler complaining about no default constructor
(1 answer)
Closed 2 years ago.
As I currently study Java in University but I am working on a C++ project. It is a text game in which I'm trying to create an empty vector of Item objects which I'm later going to add with the addItem(Item newItem) method of the Room object. The problem is that when in the constructor I try to set it so that every time a room is created, a new vector of Item objects is created with a capacity of the vector of 20, it gives me this error:
'Item::Item': no appropriate default constructor available
Here is the code:
Item.hpp:
#include <iostream>
#include <string>
#ifndef Item_HPP
#define Item_HPP
class Item {
std::string description;
public:
Item(std::string description);
std::string getDescription();
void setDescription(std::string description);
void use();
};
#endif
Item.cpp:
#include "Item.hpp"
Item::Item(std::string description) {
this->description = description;
}
std::string Item::getDescription() {
return this->description;
}
void Item::setDescription(std::string description) {
this->description = description;
}
void Item::use() {
std::cout << "You are using item: " << description << std::endl;
}
Room.hpp:
#include <iostream>
#include <string>
#include <vector>
#include "Item.hpp"
#ifndef Room_HPP
#define Room_HPP
class Room {
std::string description;
static const int MAX_ITEMS = 20;
std::vector <Item> items;
int numberOfItems;
public:
Room(std::string description);
std::string getDescription();
void addItem(Item newItem);
std::vector<Item> getItems();
Item getItem(std::string description);
};
#endif
Room.cpp:
#include "Room.hpp"
Room::Room(std::string description) {
this->description = description;
this->items = std::vector<Item>(20);
this->numberOfItems = 0;
}
std::string Room::getDescription() {
return this->description;
}
void Room::addItem(Item newItem) {
if (numberOfItems < MAX_ITEMS) {
this->items[numberOfItems] = newItem;
numberOfItems++;
}
}
std::vector<Item> Room::getItems() {
return this->items;
}
Item Room::getItem(std::string description) {
int i = 0;
bool found = false;
Item *target = NULL;
while (!found && i < numberOfItems) {
if (items[i].getDescription() == description) {
target = &items[i];
found = true;
}
++i;
}
return *target;
}
You can firstly just reserve() buffer for 20 elements and later add elements via push_back().
Room::Room(std::string description) {
this->description = description;
this->items = std::vector<Item>();
this->items.reserve(20);
this->numberOfItems = 0;
}
void Room::addItem(Item newItem) {
if (numberOfItems < MAX_ITEMS) {
if (numberOfItems == static_cast<int>(this->items.size())) {
this->items.push_back(newItem);
} else if (numberOfItems < static_cast<int>(this->items.size())) {
this->items[numberOfItems] = newItem;
} else {
// unsupported to create gap between items
}
numberOfItems++;
}
}
I have a struct of slotAndId struct, which is declared like so
typedef struct {
int slot;
int id;
} slotAndId;
Then I have a vector which holds many objects of type slotAndId...
slotAndId object;
vector<slotAndId> ids;
for (int i = 0; i < 20; i++) {
object.slot = i;
object.id = i + 2000; //random id as example, this will be generated by something else in reality.
ids.push_back(object);
}
If I then wanted to find, for example, if there's a slotAndId object which has slot equal to 20 within the vector, how would I do that in C++98? How would I then be able to delete that specific slotAndId object from the vector?
This is what std::find_if is for.
bool HasSlot20(const slotAndId& item)
{
return item.slot == 20;
}
int main()
{
std::vector<slotAndId> ids = {..};
std::vector<slotAndId>::const_iterator it = std::find_if(
ids.begin(),
ids.end(),
HasSlot20
);
}
We need the extra function because C++98 doesn't have lambdas, but we can make it more flexible by using a functor instead:
struct HasSlot
{
HasSlot(const int id) : m_id(id) {}
bool operator()(const slotAndId& item)
{
return item.slot == m_id;
}
private:
const int m_id;
};
int main()
{
std::vector<slotAndId> ids = {..};
std::vector<slotAndId>::const_iterator it = std::find_if(
ids.begin(),
ids.end(),
HasSlot(20)
);
}
or:
int main()
{
HasSlot finder(20);
std::vector<slotAndId> ids = {..};
std::vector<slotAndId>::const_iterator it = std::find_if(
ids.begin(),
ids.end(),
finder
);
}
Now this logic is re-usable with different parameters.
Alternatively just have a loop!
If your container is very large, you might consider a different (or additional) data structure that can do this in better than linear time.
The QByteArray documentation states that it is to be used when memory conservation is critical. I've been working on a class (FieldName) that keeps an internal store of commonly used QString's so that when two FieldName objects are compared for equality, a quick integer comparison is done.
My thinking was that I could save some memory and gain speed by using QByteArray instead of QString since I only need standard ASCII characters. But when I change out the QString in the following code for QByteArray, my tests show that speed is increased, but memory usage more than doubles. This runs counter to the documentation.
#ifndef H_FIELDNAME
#define H_FIELDNAME
#include <QString>
class FieldName
{
public:
FieldName() : mKey(0) { init(); }
FieldName(const QString& s);
const QString& constString() const;
bool operator==(const FieldName& other) const { return mKey == other.mKey; }
bool operator!=(const FieldName& other) const { return mKey != other.mKey; }
private:
int mKey;
void init();
int findKey(const QString& s);
};
#endif
CPP file:
#include "fieldname.h"
// #include <QMutex> // demo version not thread-safe
#include <QVector>
static QVector<QString*> FieldName__List;
static bool FieldName__FirstCall(true);
FieldName::FieldName(const QString& s)
{
init();
mKey = findKey(s);
}
const QString& FieldName::constString() const
{
return(*FieldName__List.at(mKey));
}
void FieldName::init()
{
if(FieldName__FirstCall)
{
if(FieldName__List.isEmpty())
{
FieldName__List.append(new QString(""));
FieldName__FirstCall = false;
}
}
}
int FieldName::findKey(const QString& s)
{
if(s.isEmpty())
return 0;
int sz(FieldName__List.size());
for(int idx=1; idx<sz; ++idx)
{
if(s == *FieldName_List.at(idx))
return idx;
}
FieldName__List.append(new QString(s));
return(FieldName__List.size() - 1);
}
Can anyone let me know what I may be doing that is causing this to use more memory when I substitute QByteArray for QString in the above class?
Testcode:
QStringList qsl; // filled with 84000 unique English words
FieldName fn;
foreach(QString s, qsl)
{
fn = FieldName(s);
// do stuff here
}
I have successfully populated linked list with data from a text file. Linked list contains structure that has 5 fields of type string.
I wish to sort the list by a certain structure field ( ascending or descending ). I have decided to overload operator< but I do not know how to implement it.
So far I am able to sort list by one fixed field. Here is the relevant code:
#include <iostream>
#include <list> // std::list
#include <fstream> // std::ifstream, std::ofstream
#include <string> // std::string
#include <algorithm> // std::remove, std::remove_if
#include <sstream> // std::istringstream
class Lista
{
private:
struct Person
{
// data from file
std::string lastName; // other fields omitted for keeping brevity
// operator < ( needed for correctly sorting the linked list )
bool operator< ( const Person &p )const
{
return lastName > p.lastName;
}
// constructor / destructor ... omitted for keeping brevity
};
// linked list that holds all the data from files
std::list<Person> persons;
public:
// constructor / destructor ... omitted for keeping brevity
// method for sorting the list
void sortList()
{
persons.sort();
}
};
I would like to add enum of choices, so I can use only one overloaded operator< for sorting.
Something like this:
class Lista
{
private:
struct Person
{
//================ here I could add enum of choices ============//
enum choice { FIELD1, LASTNAME, FIELD2 }; // you get the point
bool ascending; // decides if it is ascending or descending
//==============================================================//
// data from file
std::string lastName; // other fields omitted for keeping brevity
// operator < ( needed for correctly sorting the linked list )
bool operator< ( const Person &p )const
{
if ( choice == FIELD1 )
return field1 < p.field1;
if ( choice == FIELD2 )
return field2 < p.field2;
if ( choice == LASTNAME )
return lastName > p.lastName;
}
// constructor / destructor ... omitted for keeping brevity
};
// linked list that holds all the data from files
std::list<Person> persons;
public:
// constructor / destructor ... omitted for keeping brevity
// method for sorting the list
void sortList( Person::choice ch, bool ascending)
{
// here I should use the parameters to invoke proper sorting
persons.sort();
}
EDIT:
I have tried changing operator< into a function but have failed:
// this function is inside of my private struct
bool compare( const Person &p1, const Person &p2 )const
{
return p1.lastName > p2.lastName;
}
Then I called it in method like this:
void sortList()
{
persons.sort( compare );
}
But I get the following error: error C2065: 'compare' : undeclared identifier
I really have no other ideas on how to do this, can you please help me?
enum sort_choice { FIELD1, LASTNAME, FIELD2 } ; // had to put it outside to make it accessible to main()
class Lista
{
private:
struct Person
{
std::string field1;
std::string field2;
std::string lastName;
Person(const string& f1,const string& f2,const string& lname)
{
field1=f1;
field2=f2;
lastName=lname;
}
};
struct cmp_Person // A functor for comparison
{
bool cmp_ascending;
sort_choice cmp_choice;
cmp_Person(const bool& asc,const sort_choice& ch)
{
cmp_ascending=asc;
cmp_choice=ch;
}
bool operator ()(const Person &first, const Person& second) const
{
if(cmp_ascending)
{
if ( cmp_choice == sort_choice::FIELD1 )
return first.field1 < second.field1;
if ( cmp_choice == sort_choice::FIELD2 )
return first.field2 < second.field2;
if ( cmp_choice == sort_choice::LASTNAME )
return first.lastName > second.lastName;
}
else
{
if ( cmp_choice == sort_choice::FIELD1 )
return first.field1 > second.field1;
if ( cmp_choice == sort_choice::FIELD2 )
return first.field2 > second.field2;
if ( cmp_choice == sort_choice::LASTNAME )
return first.lastName > second.lastName;
}
return true;
}
};
std::list<Person> persons;
public:
void sortList( const bool& ascending, const sort_choice& ch)
{
persons.sort(cmp_Person(ascending,ch));
}
void push_to_list(const string& f1,const string& f2,const string& lname)
{
persons.push_back(Person(f1,f2,lname));
}
void display_list()
{
list<Person>::iterator it;
for(it=persons.begin(); it!=persons.end(); it++)
{
cout<<(*it).field1<<" "<<(*it).field2<<" "<<(*it).lastName<<"\n";
}
}
};
int main()
{
Lista Lobj;
Lobj.push_to_list("a","b","c");
Lobj.push_to_list("d","e","f");
Lobj.push_to_list("g","h","i");
Lobj.push_to_list("j","k","l");
Lobj.sortList(true,sort_choice::FIELD1);
Lobj.display_list();
cout<<"\n\n";
Lobj.sortList(false,sort_choice::FIELD1);
Lobj.display_list();
}
You can use something like this :
class Lista
{
public:
enum class Sort_Type{Name,LastName};
void sort(Sort_Type type,bool asc=true)
{
switch (type) {
case Sort_Type::Name:
this->list_.sort([&](const Person& p1,const Person& p2){
if(asc)
return p1.name.compare(p2.name)>=0 ;
else
return p1.name.compare(p2.name)<0;
});
break;
case Sort_Type::LastName :
this->list_.sort([&](const Person& p1,const Person& p2){
if(asc)
return p1.lastName.compare(p2.lastName)>=0 ;
else
return p1.lastName.compare(p2.lastName)<0;
});
break;
}
}
private:
struct Person
{
string name ;
string lastName;
};
std::list<Person> list_;
};
//
Lista l;
l.sort(Lista::Sort_Type::Name);
So I have an struct
struct car{
string ownerName;
float price;
int year;
};
and I declared an array of these structs
car *cars = new car[1000]
Each car has an index, for example, the car with index 0 has name John Smith.
So, my question is knowing the name of the owner how do I access the index of the car. I know that the other way i would write
cars[0].name,
to get the name, but how would i do it backwards?
Two possible ways come to my mind.
One is writing a function that finds index by name.
#include <string>
using namespace std;
car *find_by_name(car* cars, const string& name, int from, int to) {
car* car_ptr = NULL;
for(int i = from; i < to; i++) {
if (cars[i].ownerName == name) {
car_ptr = cars+i;
break;
}
}
return car_ptr;
}
As you may notice, this function is very expensive (O(n)).
The other one and the easiest one, in my opinion, is using Map or HashMap to do so.
#include <map>
#include <string>
#include <iostream>
using namespace std;
car set_new_val(map<string, car*>& my_map, const string& name, const float price, const int year) {
car* car_heap = new car();
car_heap->ownerName = name;
car_heap->price = price;
car_hep->year = year;
my_map.insert(pair<string, car*>(name, car_heap));
}
car* find_car_by_name(const map<string, car*>& my_map, const string& name) {
map<string, car*>::iterator it;
if ((it = my_map.find(name)) == my_map.end())
return NULL;
return it->second;
}
int main(int argc, char* argv[]) {
map<string, car*> my_cars_data;
set_new_val(my_cars_data, "James", 2233000.5, 2013);
set_new_val(my_cars_data, "Dave", 1222000.5, 2011);
set_new_val(my_cars_data, "Aaron", 1222000.75, 2012);
car* james_car = find_car_by_name(my_cars_data, "James");
cout << "Year " << james_car->year << endl;
car* null_car = find_car_by_name(my_cars_data, "Santa");
if (null_car == NULL)
cout << "No owner with the name Santa is recorded" << endl;
...
...
free_map(my_map);
return 0;
According to C++11, lookup for a key using Map takes O(lgn) (HashMap is O(1)), for more details read here . That's a big pro, if you handle mass of data (not to mention that it is easier to maintain).
If you use a sequential container (array, vector...) you have to search for the name. In an unsorted array a linear search is required.
// linear search
string name = "joe";
auto it = find_if(begin(cars), end(cars),
[&name](const car& c) { return c.ownerName == name; });
auto index = it - begin(cars);
If you have performance problems with this approach you could sort the array and use a binary search (preferable if your array of cars does not change) or use a associative container which gives you fast access to an element by key (map, multi_map, unordered_map...)
// binary search
struct {
bool operator()(const car& lh, const car& rh) { return lh.ownerName < rh.ownerName; };
bool operator()(const car& lh, const std::string& rh) { return lh.ownerName < rh; };
bool operator()(const std::string& lh, const car& rh) { return lh < rh.ownerName; };
} byName;
sort(begin(cars), end(cars), byName);
auto it2 = lower_bound(begin(cars), end(cars), name, byName);
if (it != end(cars) && it->ownerName == name)
{
auto index2 = it - begin(cars);
}