Howdy, Stranger!

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

Is there any way to pick highest values out of a Datamap?

A Harlowe 1.2.2 question.
So, I have a list of competitors for an election that I thought should be stored as a datamap of names, each name paired with a random value representing electability. Is there an easy way to get the name paired with the highest electability value, or better still (since Roman elections returned 2 winners) the names associated with the two highest electabily values? I've been throwing myself at it brute force without much luck. I'm pretty new to Harlowe but I've been hobby coding for a long time if that makes a difference. Any suggestions?

Comments

  • edited November 2016
    There are two main issues you need to solve to do what you want, the following is one way to do so.

    1. Iterating/looping through the members of a datamap.

    You can't do this directly but you can use the (datanames: ) macro to obtain a sorted array containing the keys of each of the members which you can Iterate/loop through instead.

    2. Harlowe currently has no loop (or equivalent) macro.

    The following example uses a (live:) macro to simulate this functionality, this is only one of the possible methods that can be used and each method has it's problems.

    note: The following example includes more $variables than are actually needed to solve the problem but I wanted to make the code as clear as possible, it also includes debugging output to show the current values of some of the variables used.
    |debug>[]
    
    (set: $competitors to (datamap: "Person 2", 20, "Person 1", 10, "Person 4", 30, "Person 3", 20))
    
    (append: ?debug)[\
    competitors datamap:
    (print: $competitors)]
    
    (set: $first to "")
    (set: $firstVotes to 0)
    (set: $second to "")
    (set: $secondVotes to 0)
    
    (set: $current to "")
    (set: $currentVotes to 0)
    
    (set: $names to (datanames: $competitors))
    (set: $length to $names's length)
    
    (append: ?debug)[
    list of names:
    (print: $names)
    
    number of names: $length
    
    loop:]
    
    (set: $index to 0)
    
    (live: 10ms)[
    	(set: $index to it + 1)
    
    	(set: $current to $names's ($index))
    	(set: $currentVotes to $competitors's ($current))
    
    	(append: ?debug)[
    (print: (text: $index) + ": " + $current + ": " + (text: $currentVotes))]
    
    	(if: $currentVotes > $firstVotes)[
    		(set: $second to $first)
    		(set: $secondVotes to $firstVotes)
    
    		(set: $first to $current)
    		(set: $firstVotes to $currentVotes)
    
    	] (else-if: $currentVotes > $secondVotes)[
    		(set: $second to $current)
    		(set: $secondVotes to $currentVotes)	
    	]
    
    	(if: $index >= $length)[
    		(stop:)
    
    		(append: ?debug)[
    
    Winners:
    (print: "First: " + $first + ": " + (text: $firstVotes))
    (print: "Second: " + $second + ": " + (text: $secondVotes))
    ]
    	]
    ]
    

    If any part of the above does not make sense then just ask for further clarification.
  • Thanks so much, Greyelf! I do understand the code and did not know that (live:) and (stop:) could control a loop. It took me a bit to set up the candidate list datamap so that there were no duplicate names from my name creator, but I just finished testing your code with that and it worked perfectly. I'm truly grateful.
  • Leon recently added a (for:) macro to the Harlowe source code, when it will be released to the general public is unknown.
  • I have a new problem greyelf. Here is your code in my program:
    <code>
    {
    (set: $first to "")
    (set: $firstVotes to 0)
    (set: $second to "")
    (set: $secondVotes to 0)


    (set: $current to "")
    (set: $currentVotes to 0)

    (set: $names to (datanames: $aedileCompetitors))
    (set: $length to $names's length)

    (set: $index to 0)

    (live: 10ms)[
    (set: $index to it + 1)

    (set: $current to $names's ($index))
    (set: $currentVotes to $aedileCompetitors's ($current))

    (if: $currentVotes > $firstVotes)[
    (set: $second to $first)
    (set: $secondVotes to $firstVotes)

    (set: $first to $current)
    (set: $firstVotes to $currentVotes)

    ] (else-if: $currentVotes > $secondVotes)[
    (set: $second to $current)
    (set: $secondVotes to $currentVotes)
    ]

    (if: $index >= $length)[
    (stop:)
    Winners:
    (print: "First: " + $first + ": " + (text: $firstVotes))
    (print: "Second: " + $second + ": " + (text: $secondVotes))

    ]
    ]}

    Winners:
    (print: "First: " + $first + ": " + (text: $firstVotes))
    (print: "Second: " + $second + ": " + (text: $secondVotes))
    </code>

    I have taken out the debugs and used my own datamap name of $aedileCompetitors to replace your $competitors. The algorithm works perfectly, but the values in $first, $firstVotes, $second, $secondVotes do not seem to last outside the loop. The winner variable display properly in the printout after (stop:). They are empty in the last printout at the end of the code here. Suggestions? I'm not quite able to use the values yet since they don't seem to have a lifespan past the loop. I must be missing something.
    Thanks again.
  • edited November 2016
    I spoke too soon, Greyelf. Even the code you gave me does pick out the winners, but the scope of the variables storing the winners does not continue outside the loop. To check this, I just copied and pasted the printed output outside the loop braces
    Winners:
    (print: "First: " + $first + ": " + (text: $firstVotes))
    (print: "Second: " + $second + ": " + (text: $secondVotes))
    

    And they are empty and 0 outside the loop. Grateful for any suggestions
  • It is simply a matter of timing, the (live:) timer event starts after the passage has been displayed, and at the time of display those variables will still have their initial value of zero. This is one of the downsides of using the (live:) macro to simulate a loop.

    One way to fix this is to add a (go-to:) macro after (stop:), the relevant variables will have the correct value within the next passage displayed.

    Another answer is to change the method being used to simulate looping. The following example uses Recursion to achieve the same outcome and it consists of two passages.

    1. The main passage where the process is started and where the outcome variables will be available.
    {
    (set: $competitors to (datamap: "Person 2", 20, "Person 1", 10, "Person 4", 30, "Person 3", 20))
    
    (set: $first to "")
    (set: $firstVotes to 0)
    (set: $second to "")
    (set: $secondVotes to 0)
    
    (set: $names to (datanames: $competitors))
    (set: $length to $names's length)
    (set: $index to 0)
    
    |workarea>[(display: "Recursive Logic")]
    }\
     \
    Winners:
    (print: "First: " + $first + ": " + (text: $firstVotes))
    (print: "Second: " + $second + ": " + (text: $secondVotes))
    
    

    2. The Recursive Logic passage, were the work is done.
    {
    (set: $index to it + 1)
    
    (if: $index <= $length)[
    
    	(set: $current to $names's ($index))
    	(set: $currentVotes to $competitors's ($current))
    
    	(if: $currentVotes > $firstVotes)[
    		(set: $second to $first)
    		(set: $secondVotes to $firstVotes)
    
    		(set: $first to $current)
    		(set: $firstVotes to $currentVotes)
    
    	] (else-if: $currentVotes > $secondVotes)[
    		(set: $second to $current)
    		(set: $secondVotes to $currentVotes)	
    	]
    
    	(replace: ?workarea)[(display: "Recursive Logic")]
    ]
    }
    
  • Again, thank you so much Greyelf! I went with the goto option. Seemed easy and reasonable. I really appreciate seeing the other code too, though; helps me learn. I'm grateful. I'll let you know when this project reaches the next rough draft.
Sign In or Register to comment.