Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Retrieving values from Javascript objects [SugarCube}

I've been trying to create a personnel database in an object with an option to retrieve random 3 persons/values. I am quite content with repeating the same code three times, since I most probably won't be needing to pull more than 3 values at once, but I just can't get over the fact I have no idea how to make that work in a loop, the way I want it to work.

As far as I can get is this:

<<set $Database = ["Person1", "Person2", "Person3", "Person4"]>>
<<for $i to 0; $i lte 3; $i++>>
<<set $Random = (0, 3)>>
<<set $RetrievedPerson1 = $Database[$Random]>> <--Here is where my problem starts. I have no idea how to format the "Retrieved Person" name the way for the loop to increase the number with each loop iteration. I am guessing it can be done easily, but I couldn't find an answer. I don't even know how to properly formulate the question.
<</for>>

I would appreciate any suggestions.




It also would be good to know how to put an object within an object in Twine and how to recover that values, but I guess I can figure out a way around that.
Sorry for my english.

Comments

  • Note: Even if tagging the opening post appropriately, you should always provide the name and version of story format you're requesting help with (e.g. SugarCube 2 beta.4). Additionally, even if using one of the Help! with … categories, you should also include the name, version, and type (if applicable) of the IDE/compiler you're using (e.g. Twine 2.0.7 executable version).


    So, is this SugarCube 2 or SugarCube 1? Please say SugarCube 2.

    Lazy Dude wrote: »
    I've been trying to create a personnel database in an object with an option to retrieve random 3 persons/values. I am quite content with repeating the same code three times, since I most probably won't be needing to pull more than 3 values at once, but I just can't get over the fact I have no idea how to make that work in a loop, the way I want it to work.

    […] <--Here is where my problem starts. I have no idea how to format the "Retrieved Person" name the way for the loop to increase the number with each loop iteration. I am guessing it can be done easily, but I couldn't find an answer. I don't even know how to properly formulate the question.
    I assume what you're trying to articulate is that you want to programmatically create $variable names, which can be done, though it requires slightly deeper knowledge of the inner workings of SugarCube. It would be simpler, though somewhat more cumbersome, to simply switch on the value of your loop index ($i) via the <<if>> macro.

    That said, if you're only going to be pulling small numbers of items, why do you need a loop in the first place? You can simply pull the requsite number of members, no need for a loop.


    No Loop Examples
    /*
    	Since $Database is an array, you can use either the <Array>.random() method
    	or the either() function to return a random member.
    */
    
    /* Using <Array>.random() */
    <<set $RetrievedPerson1 to $Database.random()>>
    
    /* Using either() */
    <<set $RetrievedPerson1 to either($Database)>>
    
    So, to retrieve three members from $Database, and if duplicates (i.e. the same random value multiple times; which would also be an issue using a loop) are okay, you could simply do something like the following:
    <<set $Database to [ "Person1", "Person2", "Person3", "Person4" ]>>
    <<set $RetrievedPerson1 to $Database.random()>>
    <<set $RetrievedPerson2 to $Database.random()>>
    <<set $RetrievedPerson3 to $Database.random()>>
    

    If duplicates are not okay, you want unique pulls each time, then the simplest thing you can do is to remove each member as it's chosen. You can achieve this with the <Array>.pluck() method.

    For example, if you are setting up $Database every time (i.e. it's okay to remove members from it), then you can do the following:
    <<set $Database to [ "Person1", "Person2", "Person3", "Person4" ]>>
    <<set $RetrievedPerson1 to $Database.pluck()>>
    <<set $RetrievedPerson2 to $Database.pluck()>>
    <<set $RetrievedPerson3 to $Database.pluck()>>
    

    On the other hand, if $Database is something you setup once (i.e. it's not okay to remove members from it), then you can make a copy and pluck members from that:
    /* Assuming $Database is already setup, this creates a shallow copy of it. */
    <<set $DbCopy to $Database.slice(0)>>
    
    /* Now, simply pluck members from the copy. */
    <<set $RetrievedPerson1 to $DbCopy.pluck()>>
    <<set $RetrievedPerson2 to $DbCopy.pluck()>>
    <<set $RetrievedPerson3 to $DbCopy.pluck()>>
    


    Loop Example
    Now that I've covered how you should probably do it, here's how you could use a loop to do by programmatically accessing the $variable names:
    <<silently>>
    /*
    	Wrapping this in <<silently>> because you probably don't want whitespace
    	generated during the loop (though there are other ways to suppress that).
    */
    
    /* Retrieves 4 persons. Note the initial value of $i. */
    <<for $i to 1; $i lte 4; $i++>>
    	<<set state.active.variables["RetrievedPerson" + $i] to $Database.pluck()>>
    <</for>>
    
    <</silently>>
    
    Again, if $Database should not be modified, then pluck from a copy, like so:
    <<silently>>
    <<set $DbCopy to $Database.slice(0)>>
    <<for $i to 1; $i lte 4; $i++>>
    	<<set state.active.variables["RetrievedPerson" + $i] to $DbCopy.pluck()>>
    <</for>>
    <</silently>>
    

    Lazy Dude wrote: »
    It also would be good to know how to put an object within an object in Twine and how to recover that values, but I guess I can figure out a way around that.
    You used "object" before to refer to an Array (which is technically true, Arrays are objects), but most of the time when you say "object" we're going to assume you mean Objects (generic objects). So, are you talking about Arrays or Objects?

    Here are some examples (no commentary though):

    Arrays
    <<set $NestedArrayDatabase to [
    	[ "Person1", "Person2" ],
    	[ "Person3", "Person4" ]
    ]>>
    <<print $NestedArrayDatabase[0][0]>> /* Prints "Person1". */
    <<print $NestedArrayDatabase[0][1]>> /* Prints "Person2". */
    <<print $NestedArrayDatabase[1][0]>> /* Prints "Person3". */
    <<print $NestedArrayDatabase[1][1]>> /* Prints "Person4". */
    

    Objects
    <<set $NestedObjectDatabase to {
    	Group1 : {
    		Member1 : "Person1",
    		Member2 : "Person2"
    	},
    	Group2 : {
    		Member1 : "Person3",
    		Member2 : "Person4"
    	}
    }>>
    <<print $NestedObjectDatabase.Group1.Member1>> /* Prints "Person1". */
    <<print $NestedObjectDatabase.Group1.Member2>> /* Prints "Person2". */
    <<print $NestedObjectDatabase.Group2.Member1>> /* Prints "Person3". */
    <<print $NestedObjectDatabase.Group2.Member2>> /* Prints "Person4". */
    
  • edited July 2015
    Hi TheMadExile,

    Thanks for this very useful answer.

    However, I'm having trouble with either datastructure, or syntax after using the insight I got from your post.

    I have a $map variable with locations nested in it, each location being an object containing stuff. I could not set-up $map as an array containing objects, because Twine then says there's an error ("Error: <<set>>: bad evaluation: Unexpected token").

    I changed $map to an object, and then it works.

    However, I'm trying to build a loop to print all this, location by location, which seems impossible to do with the object structure, unless there's a way to reference each Location object in the Map object by an index value (like an array has), which i'm not sure there is for an object.

    Here is my map thingy set up:
    <<silently>>
    <<set $map to {
    	tavern : {
        	name : "tavern",
            description : "//The local tavern, where one can drink, eat, meet people and sleep.//",
            x : 0,
            y : 0,
            z : 0,
            visited : true,
            locked : false
            },
    	taverncellar : {
        	name : "tavern's cellar",
            description : "//A tavern's cellar is usually used to store all kinds of goods destined the custormers.//",
            x : 0,
            y : 0,
            z : -1,
            visited : false,
            locked : true
            },
     }>>
    <</silently>>
    

    And the loop I originally set up when I thought I could nest objects inside arrays:
    <<for $i to 0; lte $map.length; i++>>
    	<<print ($i + 1) + ". " + $map[$i].name>>
        <<print $map[$i].description>>
        <<print "x: " + $map[$i].x>>
        <<print "y: " + $map[$i].y>>
        <<print "z: " + $map[$i].z>>
        <<print "Visited: " + $map[$i].visited>>
        <<print "Locked: " + $map[$i].locked>>
    
    <</for>>
    

    Is there any solution to this issue (either a way to reference an object's index if there is one, or nesting objects inside an array) ?
  • You can store generic objects within arrays, so you must have simply done something wrong (I can't say what, because you didn't include an example of the attempt which gave you the error). Also, your array loop is broken.

    For what you're doing, it probably makes more sense to use an object containing objects. A loop to print all of the locations using such a structure could look something like the following:
    <<set $tmpKeys to Object.keys($map)>>\
    <<for $i to 0; $i lt $tmpKeys.length; $i++>>
    ''<<print '["' + $tmpKeys[$i] + '"]'>>''
    name: <<print $map[$tmpKeys[$i]].name>>
    description: <<print $map[$tmpKeys[$i]].description>>
    x: <<print $map[$tmpKeys[$i]].x>>
    y: <<print $map[$tmpKeys[$i]].y>>
    z: <<print $map[$tmpKeys[$i]].z>>
    visited: <<print $map[$tmpKeys[$i]].visited>>
    locked: <<print $map[$tmpKeys[$i]].locked>>
    
    <</for>>\
    <<unset $tmpKeys>>\
    

    Assuming that this is for debugging or something, here's a somewhat more nicely formatted version using tables:
    <<set $tmpKeys to Object.keys($map)>>\
    <<for $i to 0; $i lt $tmpKeys.length; $i++>>----
    |>|!<<print '["' + $tmpKeys[$i] + '"]'>> |h
    | name:|<<print $map[$tmpKeys[$i]].name>> |
    | description:|<<print $map[$tmpKeys[$i]].description>> |
    | x:|<<print $map[$tmpKeys[$i]].x>> |
    | y:|<<print $map[$tmpKeys[$i]].y>> |
    | z:|<<print $map[$tmpKeys[$i]].z>> |
    | visited:|<<print $map[$tmpKeys[$i]].visited>> |
    | locked:|<<print $map[$tmpKeys[$i]].locked>> |
    <</for>>\
    <<unset $tmpKeys>>\
    
  • edited July 2015
    When trying to store my opbjects in an array, I simply switched the <<set $map to { ... } >> to <<set $map to [ ... ] >>

    This may very well be why it didn't work?

    Anywhay, thanks a lot for your code, it works very well and now I just have to understand it ;p

    So I have some questions about it :
    What does the function Object.keys($variable) do exactly?
    What do the pipes | do?
    what's |h and |! do?
    What do the \ do ?
  • RaHaN wrote: »
    When trying to store my opbjects in an array, I simply switched the <<set $map to { ... } >> to <<set $map to [ ... ] >>

    This may very well be why it didn't work?
    If you mean that you did something like the following:
    <<set $map to [
    	tavern : {
    		/* tavern object properties */
    	},
    	taverncellar : {
    		/* taverncellar object properties */
    	},
    ]>>
    
    Then yes, that's why it didn't work. You do not provide property names to the array literal. It should have been something like this:
    <<set $map to [
    	{
    		/* tavern object properties */
    	},
    	{
    		/* taverncellar object properties */
    	},
    ]>>
    

    RaHaN wrote: »
    What does the function Object.keys($variable) do exactly?
    Put simply, it returns an array of the given object's own property names (a.k.a. keys). Objects consist of key/value pairs. For example:
    <<set $variable to {
    	KEY : VALUE,
    }>>
    
    In the case of Object.keys($map), it returns an array of the names of each object you've stored in $map (e.g. tavern and taverncellar).

    RaHaN wrote: »
    What do the pipes | do?
    what's |h and |! do?
    As I noted with the example, they're part of the table markup. Specifically, the TiddlyWiki table markup (I could have used HTML table markup instead).

    SugarCube's markup language (docs: for SugarCube 2.x or for SugarCube 1.x) is based on TiddlyWiki markup, though there are some differences.

    RaHaN wrote: »
    What do the \ do?
    It's the line continuation character (see: Line Continuations, for SugarCube 2.x or for SugarCube 1.x).
  • I'm probably not the first one to tell you this (at least I hope so), but you are one fine gentleman, The MadExile ! Thanks for your availability, your guidance, and your very clear answers.
Sign In or Register to comment.