.deleteAt Shifting Index and Messing up My Dern List!

Please specify version and format if asking for help, or apply optional tags above:
Twine Version:2.3.13
Story Format: Sugarcube 2.34.1

So my question revolves around arrays and for loops. I’m trying to make a simple shop where players can buy and sell their items and it’s the part where they sell items currently giving me trouble.

I’m using a for loop to print all the items in the inventory and make them into links that, when clicked, add the amount of money to the total and delete the item from the array. The issue is that when the item is deleted it shifts the index of the items so that it fills the empty space which means the text you click isn’t necessarily when you’re selling. I’m essentially wondering if there’s a way to refresh the captured _i to regenerate the text to account for the shifted index.

My code looks like this currently and I can’t figure out if I’ve done something incorrectly or am just striaght up using the wrong tools for this job.

Edit: Fixed the code display. Thanks for the heads up. I didn’t even notice!

<<nobr>><<set $money to 100>>
<<set $what to [
 	"a crab rangoon",
 	"a gaggle of geese",
 	"an aardvark",
 	"the world's smallest violin"
 ]>>
<</nobr>>
 
 You have <span id="money"><<print $money>></span>g remaining.
 You have <span id="inv"><<print $what>></span> in your pockets.
 
 <<for _i to 0; _i lt $what.length; _i++>>
 	<<capture _i>>
 	<<linkreplace "$what[_i]">>
 		Sold <<print $what[_i]>>
 		<<run $what.deleteAt(_i)>>
 		<<replace "#inv">><<print $what>><</replace>>
 		<<set $money += 10>><<replace "#money">><<print $money>><</replace>>
 	<</linkreplace>>
 	<</capture>>
 <</for>>

It seems like what’s happening is this:
0 A
1 B
2 C

If I then click B and cause it to be deleted from the array, C is moved to index 1, but the link for C remains the same on the page and when it’s clicked there’s nothing to delete so it comes back [undefined].

Unfortunately, this forum tends to “eat” code within angle brackets. In order to get around that you need to put the text within a “Preformatted text” block by using the </> button on the toolbar.

That said, I’m going to guess that you may be missing a <<capture>> macro, which would allow you to “capture” the value of _i at the point in time at which the code within the <<for>> loop was executed. Without that, then the current value of _i at the time the user clicked would be used instead, which would lead to unexpected behavior. So, you likely need a <<capture _i>> as the first command within the <<for>> loop, and a <</capture>> just before the <</for>>.

If that doesn’t solve the problem, please fix the code in your post and post a reply noting that it was updated so that we can then look into what’s happening.

If that does solve the problem, however the link to the documentation or my explanation above doesn’t clarify enough on how the <<capture>> macro works, you might also want to take a look at the "<<capture>> Macro Help" section of my Twine/SugarCube sample code collection for a further explanation of how that macro works.

Hope that helps! :slight_smile:

It’s generally not a good idea to modify arrays as you’re indexing through them with a for loop because it leads to unpredictable results.

I can only see part of your code because of how the forum ate it, but I don’t see any reason to delete members as you’re indexing through it. What I would do instead is just define the array as empty after the loop if you plan on reusing the variable, but don’t alter it during the loop.

To explain what is making your indexing go weird as it stands, it’s going like this:

  • For the example we’ll use an array “A, B, C, D, E”.
  • You first start a loop with the index set to 0.
  • Index 0 is “A”.
  • You print “A”.
  • You delete at 0, which makes the array “B, C, D, E”.
  • The loop cycles, which increases the index to 1.
  • Index 1 is C.
  • You print “C”.
  • You delete at 1, which makes the array “B, D, E”.
  • The loop cycles, which increases the index to 2.
  • Index 2 is E.
  • You print “E”.
  • You delete at 2, which makes the array “B, D”.
  • The loop cycles, which increases the index to 3.
  • The array length is now 2 which is less than the index of 3, so the loop ends.

You should also use the <<capture>> macro like HiEv suggested as that’s the other piece to the puzzle.

Updated the code so it looks proper. Sorry about that!

Oh. Now that I can read your code, it seems my guess was a bit off base.

What you may need to do is turn the section with the for loop that outputs the inventory list into a widget, then in the <<linkreplace>> call you should make a <<replace>> call targeting the inventory list and call the widget again so that it updates the inventory list with the new inventory data.

The following is one possible way to solve your issue. It uses a temporary variable to track the ‘current’ item the link is being generated for, and it uses the <array>.delete() function instead to do the removal of that ‘current’ item.

<<nobr>><<set $money to 100>>
<<set $what to [
 	"a crab rangoon",
 	"a gaggle of geese",
 	"an aardvark",
 	"the world's smallest violin"
 ]>>
<</nobr>>
 
 You have <span id="money"><<print $money>></span>g remaining.
 You have <span id="inv"><<print $what>></span> in your pockets.
 
 <<for _i to 0; _i lt $what.length; _i++>>
	<<set _item to $what[_i]>>
 	<<capture _item>>
 	<<linkreplace "_item">>
 		Sold <<print _item>>
 		<<run $what.delete(_item)>>
 		<<replace "#inv">><<print $what>><</replace>>
 		<<set $money += 10>><<replace "#money">><<print $money>><</replace>>
 	<</linkreplace>>
 	<</capture>>
 <</for>>

That did it! Thank you so much!