tmedwards / sugarcube-2

SugarCube is a free (gratis and libre) story format for Twine/Twee.
https://www.motoslave.net/sugarcube/2/
BSD 2-Clause "Simplified" License
187 stars 42 forks source link

cycle macro: optionsfrom seems backwards #41

Closed boyland closed 4 years ago

boyland commented 4 years ago

It seems that SugarCube recently added <<cycle>>. That's great! When I was using Harlowe, I had to simulate it with a lot of complex stuff. It's almost what I want, except that optionsfrom seems to handle arrays (and maps and generic objects) in the opposite way that I was expecting, and need. In particular, it makes sense to me to have the variable set to the index/key and that the label (visible to the reader) should be the value/element. Of course, people have presumably already been using <<cycle>>, so it seems the best thing for me to do is to define a <<cycle*>> custom macro that does it the way that seems to make more sense to me. But I wanted to check to see the reasoning behind the current macro.

tmedwards commented 4 years ago

It seems that SugarCube recently added <<cycle>>.

If by recently you mean almost a year ago now, sure.

It's almost what I want, except that optionsfrom seems to handle arrays (and maps and generic objects) in the opposite way that I was expecting, and need. In particular, it makes sense to me to have the variable set to the index/key and that the label (visible to the reader) should be the value/element.

I appreciate you want it to work in a particular way, but I think you're overlooking, IME, more common use cases.

In the case of generic objects and Maps. How would that work when their values don't make sense as the label, either because they simply don't or because they're objects of some type without a sensible .toString() method.

In the case of Arrays and Sets, I'll just cover probably the most common use case—by far. How would that work when you simply want to be able to chose a value from the Array or Set.

What you seem to want is simply not the most common use cases, IME, which is what the <<cycle>> and <<listbox>> macros were coded to handle by default.

That aside. You really don't need a custom macro to get what you want, unless you simply want to write one. You should simply be able to invert your indices/keys and values for the <<optionsfrom>> tag.

For example, you can use <Array>.reduce() with a Map to invert any of the mentioned data structures so it's used as you prefer.

Array example:

<<set _choicesArray to [
    "A",
    "B",
    "C"
]>>
<<cycle "$choiceFromArray" autoselect>>
    <<optionsfrom
        _choicesArray.reduce(function (map, value, i) {
            map.set(value, i);
            return map;
        }, new Map())
    >>
<</cycle>>

Set example:

<<set _choicesSet to new Set([
    "A",
    "B",
    "C"
])>>
<<cycle "$choiceFromSet" autoselect>>
    <<optionsfrom
        Array.from(_choicesSet).reduce(function (map, value, i) {
            map.set(value, i);
            return map;
        }, new Map())
    >>
<</cycle>>

Generic object example:

<<set _choicesObj to {
    "0" : "A",
    "1" : "B",
    "2" : "C"
}>>
<<cycle "$choiceFromObj" autoselect>>
    <<optionsfrom
        Object.entries(_choicesObj).reduce(function (map, kvPair) {
            map.set(kvPair[1], kvPair[0]);
            return map;
        }, new Map())
    >>
<</cycle>>

Map example:

<<set _choicesMap to new Map([
    [0, "A"],
    [1, "B"],
    [2, "C"]
])>>
<<cycle "$choiceFromMap" autoselect>>
    <<optionsfrom
        Array.from(_choicesMap.entries()).reduce(function (map, kvPair) {
            map.set(kvPair[1], kvPair[0]);
            return map;
        }, new Map())
    >>
<</cycle>>
boyland commented 4 years ago

Thank you. I appreciate the thoughtful and detailed reply. In the event, I wrote a custom macro; it ends up being very short, I can write:

089     int free = <<cyclearray "$freei" setup.free>>;
090     E pivot = source[free];
091     <<cyclearray "$adjloi" setup.adjlo>>
092     <<cyclearray "$adjhii" setup.adjhi>>

And then test $freei etc later. (I'm using this for a code-writing exercise. The student chooses what to do for each part and then I use the choices to select a JUnit test that will fail.)