Remove a single instance from a List

Monkey Forums/Monkey Programming/Remove a single instance from a List

Bob(Posted 2013) [#1]
I have a List called Inventory that is made up of Item Objects. When an Item is added to the List I just do Inventory.AddLast(Item), but when I try to remove an Item, it removes all instances of that Item instead of just 1.

Global ItemList:List<Item> = New List<Item>
Global Inventory:List<Item> = New List<Item>

Class Item
   Field Description:String
   Field Value:int

   Function Create:Void(description:String,value:int)
       For Local Item:Item = New item
       Item.Description = description
       Item.Value = value
       ItemList.Addlast(Item)
   End Function

End Class

'Create Some Items
Item.Create("Hat",10)
Item.Create("Pants",20)
Item.Create("Shoes",30)

'Add 2 of each Item to the Inventory List
For Local Item:Item = Eachin ItemList
      Inventory.AddLast(Item)
      Inventory.AddLast(Item)
Next

Local Item:Item = Inventory.First()
Inventory.Remove(Item)


Inventory.Remove(Item) takes out Both of the Hat entries instead of just 1. Is there a way to only remove the first instance of 'Hat" in the list?


EdzUp(Posted 2013) [#2]
couldnt you do:
Local Count =0
For Local Item:Item = Eachin ItemList
   if Count =0 Inventory.Remove( Item )
   Count += 1
Next


This way it will remove the first one from the list and leave the rest intact?


Jesse(Posted 2013) [#3]
can you please try posting some executable code next time as there are a bunch of syntax errors in your code that would defer others from helping you. Welcome to the forums :).

there is something very wrong with this lines:
      Inventory.AddLast(Item)
      Inventory.AddLast(Item)


I would advice you agains doing that as the list only stores addresses of objects so in your case the list will store the same object address in two different locations in the list. this means that if you modify one item in the list it will modify both since they are both the same object.

that goes similar for the removal of the objects. The list uses this function for removing objects from the list:


	Method RemoveEach( value:T )
		Local node:=_head._succ
		While node<>_head
			Local succ:=node._succ
			If Equals( node._data,value ) node.Remove
			node=succ
		Wend
	End



if you notice you will see that the while loop will continue until it's done comparing all of the objects.
so if the same object address is in the list more than once, it will remove all of the instances of the same object. and that's why you see that it removes two objects in your case.


Bob(Posted 2013) [#4]
Thanks for the quick and thorough response Jesse! Sorry about that. Here is something that will actually run.

Global ItemList:List<Item> = New List<Item>
Global Inventory:List<Item> = New List<Item>

Class Item
	Field Description:String
	Field Value:Int
	
	Function Create:Void(description:String,value:Int)
		Local Item:Item = New Item
		Item.Description = description
		Item.Value = value
		ItemList.AddLast(Item)
	End Function
End Class

Function Main:Int()

	'Create Items
	Item.Create("Hat",10)
	Item.Create("Shirt",20)
	Item.Create("Pants",30)
	
	'Add 2 of each object into the Inventory
	For Local Item:Item = Eachin ItemList
		Inventory.AddLast(Item)
		Inventory.AddLast(Item)
	Next
	
	'Find the 1st Hat by value and remove it
	For Local Item:Item = Eachin ItemList
		If Item.Value = 10 Then
			Inventory.Remove(Item)
			Exit
		End If
	Next
	
	'Print the remaining Items in Iventory
	For Local Item:Item = Eachin Inventory
		Print Item.Description
	Next
	Return 0
	
End Function



What you said makes perfect sense. Now I understand why the Remove() is getting rid of more than I intended. That being said, what is a correct way to approach this? How can I make an Inventory list of Item objects that can be added to and removed from?


Jesse(Posted 2013) [#5]
Treat the objects as in real life(figuratively), "instantiate" as many objects of the same class as you think you will need in the itemlist.

It all depends on the type of game you are making that will determine how you will be able to store the objects. it might help if you mention what type of game you are working on.


Bob(Posted 2013) [#6]
It is for an RPG style inventory. The player will be able to buy and sell items so the content of the inventory will constantly be changing.

If there are for instance, 50 different possible items the player can have, then the inventory must be able to hold any amount (to a limit) and combination of the those 50 items.

I suppose an Array would be an easy way to do it, but lists seemed so much more logical.


Jesse(Posted 2013) [#7]
a list would be fine. just instantiate each instance of the object with new.


Bob(Posted 2013) [#8]
At what point in the above code would New need to be used? When it is added to the Inventory List?


MikeHart(Posted 2013) [#9]
They way you remove the item is a slower method than using the node you get when you add it. Try this:

[monkeycode]
Global ItemList:List<Item> = New List<Item>
Global Inventory:List<Item> = New List<Item>

Class Item
Field Description:String
Field Value:Int
Field listNode:list.Node<Item> = Null


Function Create:Void(description:String,value:Int)
Local Item:Item = New Item
Item.Description = description
Item.Value = value
Item.listNode = ItemList.AddLast(Item)
End Function


Method Remove:Void()
Self.listNode.Remove()
End
End Class

Function Main:Int()

'Create Items
Item.Create("Hat",10)
Item.Create("Shirt",20)
Item.Create("Pants",30)

'Add 2 of each object into the Inventory
For Local Item:Item = Eachin ItemList
Inventory.AddLast(Item)
Inventory.AddLast(Item)
Next

'Find the 1st Hat by value and remove it
'Iterate through the list backwards when you want to remove several items.
For Local Item:Item = Eachin ItemList.Backwards
If Item.Value = 10 Then
Item.Remove()
Exit
End If
Next

'Print the remaining Items in Iventory
For Local Item:Item = Eachin Inventory
Print Item.Description
Next
Return 0

End Function
[/monkeycode]


Jesse(Posted 2013) [#10]
@Bob
I think what you really need is an item manager. the item manager is what will hold all of the items for the game. its like a pool that you can store all of the objects into and remove them at will. then add them to the inventory list as needed. you can create the item manager with a list as a field. you would have to fill the whole list by creating instances of as many objects as needed for the game.
example:
itemList.AddLast(new Item("hat",30))
itemList.AddLast(new Item("hat",30))
itemList.AddLast(new Item("hat",20))
itemList.AddLast(new Item("Pant",30))
itemList.AddLast(new Item("Shirt",10))



Class Item
	Field Description:String
	Field Value:Int
	
	Method New(description:String,value:Int)
		Self.Description = description
		Self.Value = value
	End Method
End Class



Bob(Posted 2013) [#11]
Thanks for the help guys! I'll give these methods a shot and see if they work for what I need. I appreciate the guidance!