Unity / C#: using lists to store numbers, delete elements - list

I have been banging my head on a problem related to lists. I am a beginner, so sorry if this is a bit unclear..
My goal is to be able to write numbers from keyboard inputs, that will be displayed in a UI element in Unity.
To do so i decided to use a list, because i was wanting to add control to the display, (for example, to add a "." every 3 digits so that they would be more easily readable, like "3.489.498").
So basically, i store new inputs in this list, then i display this list with a display.text every time there is a new digit as an input.
This works actually very well, but then i wanted to be able to delete the last typed element. So i added a backspace hotkey with a List.Remove().
And this is where the nightmare starts. The thing seems to work when i press "1" and deletes right after, but for some reasons it does not work with 2.
Error message is : "Argument out of range, parameter name: index."
I just can't wrap my head around this problem :(
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
public class BoxCreateNumber : MonoBehaviour {
public Text textDisplayNumber;
public List<int> numberList = new List<int>();
void Start () {
}
void Update () {
CollectingNumberInput ();
}
void CollectingNumberInput(){
if (Input.GetKeyDown(KeyCode.Keypad1)){
numberList.Add (1);
//numberList.Insert (numberList.Count,1);
DisplayNumber ();
} else if (Input.GetKeyDown(KeyCode.Keypad2)) {
numberList.Add (2);
//numberList.Insert (numberList.Count,2);
DisplayNumber ();
} else if (Input.GetKeyDown(KeyCode.Backspace)) {
numberList.Remove(numberList.Count);
DisplayNumber ();
}
}
void DisplayNumber(){
textDisplayNumber.text = "";
for (int i = 0; i <= numberList.Count; i++) {
textDisplayNumber.text += numberList [i];
}
}
}

You just need to read the documentation.
public bool Remove(
T item
)
Parameters
item - The object to remove from the List. The value can be null for reference types.
Instead of passing the function the object to remove, you pass the number of elements in the list. This means that if the list contains element "1" as its only element, then it will work, but only by accident.
Calling RemoveAt(numberList.Count - 1) will do what you want. RemoveAt takes the index of the element to remove, and the indices are 0-based, so the last one is Count-1.

Try this to remove the last element
numberList.RemoveAt(numberList.Count-1);

Related

RemoveAt from StructArray Ue4

I struggle a bit with deleting struct from my TArray of structs.My struct contains AudioComponent and float.I was using Array.RemoveAt(index), but what i got from this was only removing half of my struct, which is AudioComponent.
Why is that? My function Removing elements looks like this:
void RemoveArrayElement( UAudioComponent AudioComponent )
{
for( int i=0; i<Array.Num(); i++ )
{
if( AudioComponent == Array[i].AudioComponent )
{
Array.RemoveAt( i );
}
}
}
What i want to achieve is completely deleting index, AudioComponent with it's float.
There are few issues with your code. As others mentioned in comments, you should use pointers. And if I'm not mistaken, you aren't allowed to use construction like this:
UPROPERTY()
TArray<UAudioComponent> invalidArray;
You should use UPROPERTY macro, otherwise your properties could and probably will be garbage collected. UPROPERTY wiki.
Next thing is that you are changing array over which you are iterating. I wrote few approaches, let's look at them:
void RemoveArrayElement(UAudioComponent* AudioComponent)
{
TArray<UAudioComponent*> audioArray; // array will be initialized somewhere else, this is for demo purpose.
// you always should check your pointers for validity
if (!AudioComponent || !AudioComponent->IsValidLowLevel() || AudioComponent->IsPendingKill())
return;
// Correct approach 1 (multiple):
TQueue<UAudioComponent*> toDelete;
for (int i = 0; i < audioArray.Num(); i++)
{
auto item = audioArray[i];
if (AudioComponent == item || true) // we simulate another condition for multiselect
{
toDelete.Enqueue(item);
}
}
// better approach for iteration:
for (auto item : audioArray)
if (item == AudioComponent || true) // we simulate another condition for multiselect
toDelete.Enqueue(item);
// finalize deletion in approach 1
UAudioComponent* deleteItem;
while (toDelete.Dequeue(deleteItem))
audioArray.Remove(deleteItem);
// almost correct approach 2 (single) :
UAudioComponent* foundItem;
for (auto item : audioArray)
if (item == AudioComponent)
{
foundItem = item;
break; // we can skip rest - but we must be sure, that items were added to collection using AddUnique(...)
}
if (foundItem)
audioArray.Remove(foundItem);
// correct and the best - approach 3 (single)
audioArray.Remove(AudioComponent);
}
First keep in mind that comparing two objects does not necessarily lead to the expected result of equality. Using the == operator means executing a function (bool operator==(L, R);) that specifies what should happen. So if you did not overload the == operator then you don't know what using it would result to unless you look at the source code where it's defined. Since you want to remove the exact audio component and not an instance of it that looks the same, you want to use pointers in your array. That also helps performance since your are not copying the whole component when calling RemoveArrayElement(...); but a single pointer. Also when there are two identical audio components stored in the array and they are at index a and a+1, then removing the audio component at index a the next iteration would skip your second audio component since all upper indexes are decremented by one.

Visual C++ ListBox manipulation; add a string

Language: Visual C++
I have two classes. TableColumnsItemType has two variables: visibleName, and dataName (both are string).
class ColumnsSortedType is a sorted linked list.
I want to use a function called UpdateListbox to add TableColumnsItemType item to the listBox in my Form1.h (item comes from my linkedList)
public: System::Void UpdateListBox() {
//***********************************************************************
//Purpose: To add items from ColumnsSortedType::list to Form1->ListBox1
//Input: list
//Pre: list is initialized and has data.
//List gets data before Form1 initilize call, or before System::UpdateListBox call
//Output: None.
//Post: listBox is filled with data from list
//*************************************************************************
TableColumnsItemType item;
ColumnsSortedType list;
string str = "This is a sample";
item.SetDataName(str);
item.SetVisibleName();
list.InsertItem(item);
str = "This is also a sample";
item.SetDataName(str);
item.SetVisibleName();
list.InsertItem(item);
list.InsertItem(item);
int length = list.LengthIs();
int pos;
for(pos = 0; pos < length; pos++)
{
list.GetNextItem(item);
str = item.ReturnVisibleName();
//add code to insert item into listbox
this->listBox1.AddString(item); //**Here is my issue!**
}
}
I really don't want to add items to list in this function; but I want to get this function working before I start adding extra arguments to pass item and list around.
I'm having trouble adding an item to ListBox1. What is a member function that would allow me to do this? I know that Visual Basic would be something like listBox1.Items.Add
VS doesn't give me function choices when I add the dot-notation after listBox1, so I'm guessing that my syntax is incorrect. I'm having difficulty finding examples for C++ code manipulation of listboxes.

pushing an array to a function in c++?

{
int a[3];
mainClassStack.pushNumber(a[1,2,3]);
break;
}
void stack_class::pushNumber(int numberFunc)
{
if(head==NULL)
{
head = new stack_struct;
head->number = numberFunc;
head->next_number = NULL;
tailPointer=head;
}
else
{
pointerFunc = new stack_struct;
pointerFunc->number=numberFunc;
pointerFunc->next_number=NULL;
head->next_number=pointerFunc;
head=pointerFunc;
}
}
void stack_class::pushNumber(char charFunc)
{
int a=0;
a=charFunc;
if(head==NULL)
{
head = new stack_struct;
head->number = a;
head->next_number = NULL;
tailPointer=head;
}
else
{
pointerFunc = new stack_struct;
pointerFunc->number=a;
pointerFunc->next_number=NULL;
head->next_number=pointerFunc;
head=pointerFunc;
}
}
void stack_class::pushNumber(int arrayFunc[3])
{
if(head==NULL)
{
for(int i=0;i<3;i++)
{
head = new stack_struct;
head->number = arrayFunc[i];
head->next_number = NULL;
tailPointer=head;
}
}
else
{
for(int i=0;i<3;i++)
{
pointerFunc = new stack_struct;
pointerFunc->number=arrayFunc[i];
pointerFunc->next_number=NULL;
head->next_number=pointerFunc;
head=pointerFunc;
}
}
}
I am overloading functions and pushing an array into the appropriate function which will later add the values from the arrays to a dynamic linked list. So far I have written this, but when I try to print the linked list, it shows garbage. What am I doing wrong here?
Does this make more sense to you?
void Foo(int arg[])
{
printf("%i\n", arg[0]);
printf("%i\n", arg[1]);
printf("%i\n", arg[2]);
}
int main()
{
int a[3] = {1,2,3};
Foo(a);
return 0;
}
Output is:
1
2
3
To make more clear what others are pointing out, the comma operator returns the last value in the series. The value of the expression (1, 2, 3) is therefore 3, and so the expression a[1, 2, 3] is nothing more than a long way of saying a[3].
But there's something else that looks goofy right away, which is this loop here:
if(head==NULL)
{
for(int i=0;i<3;i++)
{
head = new stack_struct;
head->number = arrayFunc[i];
head->next_number = NULL;
tailPointer=head;
}
}
You point head to a new thing, set a couple things inside it, and then set tailPointer to point to that same new thing...but then immediately do the same two more times. Do you see the problem? At the end of the loop, you've created three items whose next_number values are all NULL, and head and tailPointer both point to the same thing: the last one created. You have nothing at all pointing to the first two items you created, and none of them point to each other. In other words, there is no linked list at all at the end of this operation (although there are a few new objects lying out there in memory somewhere with no way to access or de-allocate them).
You probably mean to have your if statement inside your loop, rather than the other way around, but you need to change more than just that, because the code in the other for loop might be a little problematic as well. It creates a new item, then sets up a link from the head item to the new one, but then it moves the head to point to the new one, so you are successfully adding an item to the tail of the list, but leaving yourself with a head variable that is actually pointing at the tail.
I suggest the best way to see what's going on is to take a pencil and paper, write down your variables and draw boxes to represent your objects, with their member variables inside, then step-by-step follow your code, writing in variable values or erasing and drawing arrows from the pointers to show where each one lands. I think you'll immediately see where your algorithm is going awry.
C++ does not allow to pass an entire array as an argument to a function. However, You can pass a pointer to an array by specifying the array's name without an index.
If you want to pass a single-dimension array as an argument in a function, you would have to declare function formal parameter in one of following three ways and all three declaration methods produce similar results because each tells the compiler that an integer pointer is going to be received.
Way-1
Formal parameters as a pointer as follows:
void myFunction(int *param)
{
.
.
.
}
Way-2
Formal parameters as a sized array as follows:
void myFunction(int param[10])
{
.
.
.
}
Way-3
Formal parameters as an unsized array as follows:
void myFunction(int param[])
{
.
.
.
}

For Looping Link List using Templates

Having used the various search engines (and the wonderful stackoverflow database), I have found some similar situations, but they are either far more complex, or not nearly as complex as what I'm trying to accomplish.
C++ List Looping
Link Error Using Templates
C++:Linked List Ordering
Pointer Address Does Not Change In A Link List
I'm trying to work with Link List and Node templates to store and print non-standard class objects (in this case, a collection of categorized contacts). Particularly, I want to print multiple objects that have the same category, out of a bunch of objects with different categories. When printing by category, I compare an sub-object tmpCategory (= "business") with the category part of a categorized contact.
But how to extract this data for comparison in int main()?
Here's what I'm thinking. I create a GetItem member function in LinkList.tem This would initialize the pointer cursor and then run a For loop until the function input matches the iteration number. At which point, GetItem returns object Type using (cursor -> data).
template <class Type>
Type LinkList<Type>::GetItem(int itemNumber) const
{
Node<Type>* cursor = NULL;
for(cursor = first;
cursor != NULL;
cursor = (cursor -> next))
{
for(int i = 0; i < used; i++)
{
if(itemNumber == i)
{
return(cursor -> data);
}
}
}
}
Here's where int main() comes in. I set my comparison object tmpCategory to a certain value (in this case, "Business"). Then, I run a For loop that iterates for cycles equal to the number of Nodes I have (as determined by a function GetUsed()). Inside that loop, I call GetItem, using the current iteration number. Theoretically, this would let the int main loop return the corresponding Node from LinkList.tem. From there, I call the category from the object inside that Node's data (which currently works), which would be compared with tmpCategory. If there's a match, the loop will print out the entire Node's data object.
tmpCategory = "Business";
for(int i = 0; i < myCategorizedContact.GetUsed(); i++)
{
if(myCategorizedContact.GetItem(i).getCategory() == tmpCategory)
cout << myCategorizedContact.GetItem(i);
}
The problem is that the currently setup (while it does run), it returns nothing at all. Upon further testing ( cout << myCategorizedContact.GetItem(i).getCategory() ), I found that it's just printing out the category of the first Node over and over again. I want the overall scheme to evaluate for every Node and print out matching data, not just spit out the same Node.
Any ideas/suggestions are greatly appreciated.
Please look at this very carefully:
template <class Type>
Type LinkList<Type>::GetItem(int itemNumber) const
{
Node<Type>* cursor = NULL;
// loop over all items in the linked list
for(cursor = first;
cursor != NULL;
cursor = (cursor -> next))
{
// for each item in the linked list, run a for-loop
// counter from 0 to (used-1).
for(int i = 0; i < used; i++)
{
// if the passed in itemNumber matches 'i' anytime
// before we reach the end of the for-loop, return
// whatever the current cursor is.
if(itemNumber == i)
{
return(cursor -> data);
}
}
}
}
You're not walking the cursor down the list itemNumber times. The very first item cursor references will kick off the inner-for-loop. The moment that loop index reaches itemNumber you return. You never advance your cursor if the linked list has at least itemNumber items in the list.. In fact, the two of them (cursor and itemNumber) are entirely unrelated in your implementation of this function. And to really add irony, since used and cursor are entirely unrelated, if used is ever less than itemNumber, it will ALWAYS be so, since used doesn't change when cursor advances through the outer loop. Thus cursor eventually becomes NULL and the results of this function are undefined (no return value). In summary, as written you will always either return the first item (if itemNumber < used), or undefined behavior since you have no return value.
I believe you need something like the following instead:
template< class Type >
Type LinkList<Type>::GetItem(int itemNumber) const
{
const Node<Type>* cursor = first;
while (cursor && itemNumber-- > 0)
cursor = cursor->next;
if (cursor)
return cursor->data;
// note: this is here because you're definition is to return
// an item COPY. This case would be better off returning a
// `const Type*` instead, which would at least allow you to
// communicate to the caller that there was no item at the
// proposed index (because the list is undersized) and return
// NULL, which the caller could check.
return Type();
}

Unable to access contents of initialized struct from a vector

I have a struct:
typedef struct
{
Qt::Key qKey;
QString strFormType;
} KeyPair;
Now I initialize KeyPair instantiations so I could use it for my Automated Test App.
KeyPair gTestMenu[] =
{
{ Qt::Key_1 , "MyForm" },
{ Qt::Key_1 , "SubForm" },
{ Qt::Key_Escape, "DesktopForm" }
};
KeyPair gBrowseMenu[] =
{
{ Qt::Key_1 , "MyForm" },
{ Qt::Key_2 , "Dialog" },
{ Qt::Key_Escape, "DesktopForm" }
};
and like 100 more instantiations....
Currently, I call a function which uses these KeyPairs.
pressKeyPairs( gTestMenu );
pressKeyPairs( gBrowseMenu );
and more calls for the rest...
I would like to put all these KeyPair instantiations in a vector so I wouldn't have to call pressKeyPairs() a hundred times... I'm a newbie in using vectors... so I tried:
std::vector<KeyPair, std::allocator<KeyPair> > vMasterList;
vMasterList.push_back( *gTestMenu );
vMasterList.push_back( *gBrowseMenu );
std::vector<KeyPair, std::allocator<KeyPair> >::iterator iKeys;
for(iKeys = vMasterList.begin(); iKeys != vMasterList.end(); ++iKeys)
{
pressKeyPairs(*iKeys);
}
However, this code block isn't working... :( Can somebody tell me how to properly put these KeyPairs in a vector?
You've to use insert to populate the vector with your different arrays. Here is how you should do it.
//initialized with one array
std::vector<KeyPair> vMasterList(gTestMenu, gTestMenu + 3);
//adding more items
vMasterList.insert( vMasterList.end(), gBrowseMenu , gBrowseMenu + 3);
And then reimplement your pressKeyPair function, so that you can use std::for_each from <algorithm> header file as,
//pressKeyPair will be called for each item in the list!
std::for_each(vMasterList.begin(), vMasterList.end(), pressKeyPair);
Here is how you can write the pressKeyPair function:
void pressKeyPair(KeyPair &keyPair) //takes just one item!
{
//press key pair!
}
In my opinion, this is better design, as it doesn't need "manual" loop anymore at the calling site!
You can even call pressKeyPair for first 5 items in the list as,
//pressKeyPair will be called for first 5 items in the list!
std::for_each(vMasterList.begin(), vMasterList.begin() + 5, pressKeyPair);
One more example:
//pressKeyPair will be called for 5 items after the first 5 items, in the list!
std::for_each(vMasterList.begin()+5, vMasterList.begin() + 10, pressKeyPair);
EDIT:
If you want to use manual loop, then you've to use this:
std::vector<KeyPair>::iterator it;
for( it = vMasterList.begin(); it != vMasterList.end(); ++it)
{
pressKeyPair(*it);
}
But I would say it's not as elegant as the approach described earlier. Remember, this assumes that the function pressKeyPair has this signature:
void pressKeyPair(KeyPair &keyPair); //it doesn't accept array!
I think that the problem is that the code
vMasterList.push_back( *gTestMenu );
Only adds a single element of gTestMenu to the vector, namely the first. The reason is that this code is equivalent to the following:
vMasterList.push_back( gTestMenu[0] );
From which I think it's a bit easier to see what's going wrong.
To fix this, you probably want to add all of the elements in gTestMenu to the master list. You can do this using the three-parameter vector::insert function:
vMasterList.insert(v.begin(), // Insert at the beginning
gTestMenu, // From the start of gTestMenu...
gTestMenu + kNumTests); // ... to the end of the list
Here, you'll need to specify how many tests are in gTestMenu as kNumTests. You can do the same for gBrowseMenu.
By the way, you don't need to specify the allocator type in the vector declaration if you just want to use the default std::allocator. You can just write
std::vector<KeyPair> vMasterList;
And you'll be totally fine.