Variable undefined when iterating in for loop

Twine Version: 2.9.2
Story Format: Sugarcube 2.36.1

I’m new to Sugarcube so I apologize in advance if I’m being stupid but I couldn’t find anything related to what I was searching for. I have a dictionary of dictionaries representing companies and their attributes (e.g. name, bio, and other stuff i’m going to add later). I wanted to make an expandable way to view all the company’s about sections, so I used this code:

:: Main
<<set $companies to {
"company1":{"Name":"Company 1","About":"About Company 1"},
"company2":{"Name":"Company 2","About":"About Company 2"},
"company3":{"Name":"Company 3, the best one","About":"About company 3"}
}>>

<<set $index to ["company1","company2","company3"]>>

<<for _i to 0; _i lt $index.length; _i++>>
About <<link $companies[$index[_i]]["Name"] "About">>
	<<set $about to $companies[$index[_i]]["About"]>>
<</link>>
<</for>>


:: About
<<print $about>>

When I run this, I get the error Error: <<set>>>: bad evaluation: Cannot read properties of undefined (reading 'About').
If I add a <<goto "About>> in the <<for>> loop, the error still pops up and when I close it I the “About” passage just shows [undefined]. What do I need to change to fix this?

A couple of points about your example:

1: The range variant of <<for>> could be used to simplify your indexing.

2: The content within the body of a interactive macro like <<link>> isn’t processed until that component is interacted with. Which means the temporary variable being created & set by the <<for>> macro may either: have a value other than what it was when a specific loop iteration occurred; or that temporary variable may no longer exist.

So you will need to use the <<capture>> macro to persist the values of the loop’s temporary variable so they are available when the body of the <<link>> macro is processed.

3: Bracket Notation and Dot Notation can be used at the same time, which is some cases can make coding easier.

The following is a variant of your own code with the above applied to it…

<<for _id range $index>>
	<<nobr>>
    	About
        <<capture _id>>
        	<<link $companies[_id].Name "About">>
    			<<set $about to $companies[_id].About>>
    		<</link>>
		<</capture>>
	<</nobr>>
<</for>>

Note: It is unclear from your example if the value of the Object stored in the $companies variable changes during playthrough of your project, or if it remains the same once initially set. eg, if the data is Dynamic/Stateful or Static/Stateless.

If the value of that variable remains the same after it is initialise then a better place to store it would be the special setup variable, as it doesn’t get persisted in any Saves.

<<set setup.companies to {
"company1":{"Name":"Company 1","About":"About Company 1"},
"company2":{"Name":"Company 2","About":"About Company 2"},
"company3":{"Name":"Company 3, the best one","About":"About company 3"}
}>>

Then that Stateless object can be used like so…

<<for _id range $index>>
	<<nobr>>
    	About
        <<capture _id>>
        	<<link setup.companies[_id].Name "About">>
    			<<set $about to setup.companies[_id].About>>
    		<</link>>
		<</capture>>
	<</nobr>>
<</for>>
1 Like