In advance, please forgive me if I do not give adequate background information for my question. Long time reader, first time asker.
I am making a program where one has a database of cars accessed through a tab delimited .txt file (we did something like this recently in my programming class, so I wanted to expand upon it).
Instead of using the terminal window, my format is displaying the Car objects (containing make, model, year, price, etc.) in ArrayList. I'm using JFrame, a JList, and a ListModel since I'm using an array of Car objects.
In my program, I wanted to create a delete method where the user could delete items from the database. Initially they would select the item from the JList and then would click on the delete button. This invokes the delete() method, which is the tab shown below...
void delete()
{
int i = list.getSelectedIndex();
String string = (String)listModel.getElementAt(i);
for(Car c : cars)
{
String year = String.valueOf(c.getYear());
String conditionReport = String.valueOf(c.getConditionReport());
String price = String.valueOf(c.getPrice());
if(c.getMake().indexOf(string) != -1 && c.getModel().indexOf(string) != -1 && year.indexOf(string) != -1 && conditionReport.indexOf(string) != -1 && price.indexOf(string) != -1 && c.getWarranty().indexOf(string) != -1 && c.getComments().indexOf(string) != -1)
{
int choice = JOptionPane.showConfirmDialog(null, "Are you sure you would like to remove the " + cars.get(i).getYear() + " " + cars.get(i).getMake() + " " + cars.get(i).getModel() + "?", "Choose One", JOptionPane.YES_NO_OPTION);
if(choice == JOptionPane.NO_OPTION || choice == JOptionPane.CLOSED_OPTION)
{
return;
} else
{
cars.remove(c);
listModel.removeElementAt(i);
}
}
}
writeFile();
}
I have pinpointed my issue to be inside the if statement. (I printed things before and after to try to find where the program is lying. 'list' is my JList and 'listmodel' is my default list model. Car is an object I created that contains the elements (as seen by the get methods). The elements shown in the listModel are merely Strings that show getMake(), getModel(), and so forth... (Each 'get' item is separated by about 10 spaces.)
What am I doing wrong in the if statement? I figured that the getMake() and getModel() (and so forth) would be substrings of the index selected.
Thank you so much for your assistance! Any input regarding ways I could make further questions more specific and clear would be greatly appreciated!
It seems like you are doing this to find the selected Car in some kind of data structure. You would be better off doing something like programming a custom list model that had access to cars itself. Then you could retrieve the selection more immediately. If cars is an ArrayList that list merely parallels I also don't see why you can't do something to the effect of cars.remove(list.getSelectedIndex());. Or since JList can display any object, override Car's toString to display what the list currently displays and have the list display Cars. Then you can cars.remove((Car)list.getSelectedValue());.
But aside from that, based on your description it sounds like you mean to do the evaluation the other way. It's the list item that should contain all of the Car attributes, rather than all of the Car attributes containing the list item. So something like
if( string.contains(c.getMake()) && string.contains(year) // and so on
(Or with indexOf but since contains merely returns indexOf > -1, using contains makes your code somewhat shorter.)
Related
I am still a newbie at C++, but I am currently working on a text based game. For my game, I have an Inventory that works, but I want to add a couple functions to it so it is better. I have tried googling around, but I can't find enough help or resources for me to be able to do this myself.
The first thing I am trying to do is when I use my delete function. For the function, I am having the user enter the item they wish to remove from their inventory. I want to be able to check that the user enters an item that exists in their inventory and not make an item up. So after the user enters the item, I take that item and go through the list, check to see if it is there, and then if it is there, delete it, and if it is not there print out an error message.
This is my code so far:
void Inventory::DeleteItem()
{
string deleteitem = " ";
if (item_numbers == 0) {
cout << "\nError: No items in the Inventory.\n\n";
}
else {
cout << "\nPlease type the item you wish to use or take out of the inventory: ";
cin >> deleteitem;
}
bool found = false;
for (int i = 0; i<item_numbers && !found; i++) {
if (Info[i].item_name == deleteitem) {
while(found == true){
cout<<"Found an item matching what you wrote: "<<Info[i].item_name<<endl;
item_numbers--;
cout<<"Item: "<<Info[i].item_name<< " is now gone." <<endl;
break;
}
}
if (found == false){
cout<<"liar"<<endl;
break;
}
}
}
I bet the solution is so simple and I am missing it. I just honestly can't see it. I'm sorry! But I appreciate all the help!
Currently when I test the code, this is how it prints out:
Which item will you be adding to your inventory? Here
Which item will you be adding to your inventory? Now
Please type the item you wish to use or take out of the inventory: Here
liar
You can't delete it in the way you thought the program.
I have done something similar a while ago and my advice is:
1. Use another class "Item" with attribute "Name" and "NumberOfItems"(or more if you want);
2. In the Inventory class add an attribute "vector<Items> MyItems" (it this way you learn about vector as well)
3. when you find the object in your list, you call MyItems.erase(position), where position is the index of your found item.
I'm working on a scheduling program, and inside the dataGridView, we have a few ComboBox Columns that are populated by 3 entries upon creation, but I wanted to be able to add more as the user creates them, but I have no idea how you would access the combobox data. Any help is appreciated!
// this is initialized in a separate part.
/* System::Windows::Forms::DataGridView^ dataGridView;*/
System::Windows::Forms::DataGridViewComboBoxColumn^ newCol =
(gcnew System::Windows::Forms::DataGridViewComboBoxColumn());
dataGridView->Columns->AddRange(gcnew cli::array< System::Windows::Forms::DataGridViewComboBoxColumn^ >(1) {newCol});
// add the choices to the boxes.
newCol->Items->AddRange("User inputted stuff", "More stuff", "Add New...");
Solution
If you have access to the data from the user entry and you know the column index for the DataGridViewComboBoxColumn, you should be able to just do the following wherever needed:
DataGridViewComboBoxColumn^ comboboxColumn = dataGridView->Columns[the_combobox_column_index];
if (comboboxColumn != nullptr)
{
comboboxColumn->Items->Add("the new user entry");
}
Comments Response
how could you change the selected index of that combobox (the one that
the edit was triggered on)? [...] we want it so that when the new item
is added the selected index is set to that new item).
Couple of ways come to mind.
Add a single line within the if-statement of the above code. This will set the default displayed value for each DataGridViewComboBoxCell in the DataGridViewComboBoxColumn.
if (comboboxColumn != nullptr)
{
comboboxColumn->Items->Add("the new user entry");
comboboxColumn->DefaultCellStyle->NullValue = "the new user entry";
}
Pros: Clean, efficient. Previous user-selected values are left intact. The cell's FormattedValue will display the new user value by default if no other selection has been made.
Cons: Doesn't actually set a cell's selected value, so Value will return null on cells not explicitly user-selected.
Actually set the value of certain cells (based on your criteria) to the user-added value.
if (comboboxColumn != nullptr)
{
comboboxColumn->Items->Add("the new user entry");
for (int i = 0; i < dataGridView->Rows->Count; i++)
{
DataGridViewComboBoxCell^ cell = dataGridView->Rows[i]->Cells[the_combobox_column_index];
if ( cell != nullptr /* and your conditions are met */ )
{
cell->Value = "the new user entry";
}
}
}
Pros: The Value of targeted cells is actually set to the new user value.
Cons: Logic deciding which cells should be affected is more complicated.
I'm writing a Jukebox simulator and I'm trying to search a vector of Album objects by album title and return the index for use in other functions. The function is to be used for a number of different things such as deleting an album, printing an album etc.
I have gotten it to work in a previous application when the function was in the same Class as the data member to search for. I can however for some reason not get it to work using getters. No matter what I input as search key the idx returns 3 although the vector only contains indexes 0, 1 and 2 (only 3 albums right now).
The lambda function seem to be able to access data by using the getAlbum()-getter but somehow the comparison doesn't work.
My approach might be entirely wrong and I'd be grateful for any pointers in the right direction, or suggestions on how to accomplish the desired result using some other technique.
int Jukebox::findAlbumIdx(string key)
{
// Get search predicate
auto it = find_if(albvec.begin(), albvec.end(), [key](Album const &a)
{
return (a.getAlbum() == key);
});
int idx = it - albvec.begin();
return idx;
}
void Jukebox::delAlbum()
{
cin.get();
string key;
cout << "Input title of album to delete: ";
getline(cin, key);
int idx = findAlbumIdx(key);
if(idx > albvec.size() - 1)
cout << "No hits." << endl;
else
albvec.erase(albvec.begin() + idx);
}
getAlbum is just a simple inline getter as such:
string getAlbum() const {return album_title;}
Following Jonathan Wakely's suggestion to add std::cout << a.getAlbum() << " == " << key << std::endl; in the lambda the output is this:
Input title of album to delete: Abbey Road
== Abbey Road
== Abbey Road
== Abbey RoadLonely Hearts Club Band
No hits.
Obviously the getter isn't actually getting much to use for comparison. Not sure why it only gets the last entry and on the right hand side of the comparison.
If I add this to any of the functions above it gets and displays the Album titles correctly. The vector seems to be fine just before calling findAlbumIdx(key); and also inside the findAlbumIdx function.
for(unsigned int i = 0; i < albvec.size(); ++i)
cout << albvec[i].getAlbum() << endl;
The original playlist file that is read into the vector to be searched had dos newlines, after converting it using dos2unix (since I'm running Linux) the search, and I presume a lot of other things, is working correctly.
I suppose trimming newline characters while reading the file into the vector would be the more correct approach.
very specific question I'm afraid (and I'm rather a novice, so apologies in advance):
I'm currently trying to finish my final project for a University object-oriented C++ course. I'm creating a student database to store exam results for students. My setup has loads of custom classes but all work perfectly (or at least do what I want them to do).
The project is set up as follows:
I have a "master" map of all "course"s, to which everything points to (so a course isn't duplicated if more than one student is taking it).
A "student" is a vector of pointers to "course"s and a corresponding double "result", and I have a master map of all students in the system.
a "degree" is a class of two vectors of pointers, one to courses offered by that degree, and one to students taking that degree. When a degree is created, it searches both master maps. If the first x letters in a course id matches the degree prefix, the course is added. If a student's subject matches the course name, the student is added.
My problem is this:
As I have some options to manually input courses and students after the initial setup from CSV files, I have writen a function to update my degrees if a course/result is added which should be included in a degree (see below). However, this code inevitably results in the first course and student being re-added (i.e. repeated) to the first degree the first time this function is called. this problem is not repeated if the function is called again. I have absolutely no idea why. A huge amount of time and cout statements later and I'm no closer to solving this. Am I missing something obvious about the first run? I may have set the loops up wrong (I'm not very familiar with maps). Don't hesitate to call me an idiot!
As I have said above, all the rest of the program is gravy, without this odd issue the program is fine. The problem does not appear to come from my print functions either.
Thank you in advance for your time.
//upgrade degrees function: used whenever new courses or students could be created by the user. It ticks through all stored degrees and scans cd and sd. If it finds an unstored course or student that should be stored, they are added.
void degree_database::update_degrees(course_database &cd, student_database &sd) {
cout << "updating degrees..." << endl;
bool found = false;
vector<degree>::iterator current;
for (current = start; current < end; ++current) {
//scan course list
map<string, course>::iterator x;
for (x = cd.get_start(); x != cd.get_end(); ++x) {
if (x->first.substr(0,3) == current->get_prefix().substr(0,3) || current->get_prefix() == "ALL") {
//check to see if course is already stored
vector<course*>::iterator a;
for (a = current->get_c_start(); a < current->get_c_end(); ++a) {
if (*a == &(x->second)) {
found = true;
break;
}
}
//if found == true, then while loop broke early (i.e. the course was already stored).
if (found == false) current->add_course(x->second);
found = false;
}
}
//scan student list
found = false;
map<string, student>::iterator y;
for (y = sd.get_start(); y != sd.get_end(); ++y) {
if (y->second.get_subject() == current->get_name() || current->get_name() == "All") {
//check to see if course is already stored
vector<student*>::iterator b;
for (b = current->get_s_start(); b < current->get_s_end(); ++b) {
if (*b == &(y->second)) {
found = true;
break;
}
}
//if found == true, then while loop broke early (i.e. the student was already stored).
if (found == false) current->add_student(y->second);
found = false;
}
}
}
cout << "done." << endl;
}
You store course by value in the course list and then you use pointer to this object in comparison. Apparently, you shoud store pointers in the map. I think (*a == &(x->second)) fails on the first run and pointer to the object from the course map is added to a degree object. On the second run, (*a == &(x->second)) succeeds and all looks ok. The same for student map.
I was coding with 2 CStringList objects. Each has its own data, for eg one has name and other the phoneno, and both are in sync, i.e, if there is a phoneno there is a name and viceversa.
Now, i have 2 combobox in which i show the names and the respective phonenos. The name combobox is sorted, hence the sync between the two goes for a toss. hence for sorting i did the following:
int aComboElementNo = myNameComboBox.GetCount();
if( aComboElementNo >= 1 )
{
for( int aIndex = 0; aIndex < aComboElementNo; aIndex++ )
{
CString aTempStr;
// Getting the string in the combobox
myNameComboBox.GetLBText( aIndex, aTempStr );
// Getting the position where the name is present in the list
POSITION aPos = aNameList.Find( aTempStr );
// setting the appropriate phoneno in the 2nd combobox
myPhoneComboBox.AddString( aPhoneList.GetAt( aPos ) );
}
}
When i executed this i got the names in the myPhoneComboBox rather than the phonenos.
Now i have 2 qns:
how come i get the name present in namelist when i am accessing the phonelist? isn't it a breach, as i am able to access some other variables data using some other variable.
how to sort the 2nd list.
I Hope U are using CStringArray and not CStringList.
You need to use FindIndex rather than Find since Find will return OBJECT Pos rather than the Index count....
and to get the element with array use simply [] the operator.
If You still want to use CStringList then through Iterator Find the Index Count of the first match of string in one List and Use FindIndex of that IndexCount to get the postition object for the secondlist to use GetAt to the second list.
Why do you have 2 separate lists? Why not one CTypedPtrArray of structures holding both the name and the phone nb?
That is a crazzy, forgive me, stupid way to find names. It assumes the names are unique. God help me, I've had to deal with these things, name fields should never be viewed as unique, its bloody dangerious. Just ask my dad Baash05 Sr.
I'd assume there's an ID or some data set as the app adds to the combo box. Please use that in your map. My guess is the programmer set the data to either the ID of the name, or a pointer to the object that contained the name. (person object/business object/student object...).
If the code that adds the names didn't add a way to tell the difference between George Forman and any of his kids, then make an argument to the boss, that it's implementation should be changed, because by god it should be!
int aComboElementNo = myNameComboBox.GetCount();
for( int aIndex = 0; aIndex < aComboElementNo; aIndex++ )
{
int nameLocal = myNameComboBox.GetItemData( aIndex);
myPhoneComboBox.InsertString(aIndex, aPhoneList[namelocal] );
}