Question about countWith() and predicates.

Twine Version: 2.0.0
sugarcube version: 2.36.1

Hello, this is the first time I am using the forum so I apologize if there is any etiquette I am lacking. I am curious about how to use the countWith() method for arrays, specifically how to have multiple conditionals. To demonstrate, I will slightly modify the example they give in the documentation.

// Given: $items = [
// 	{ name : 'Healing potion', kind : 'weakPotion' },
//  { name : 'Healing potion', kind : 'strongPotion' },
// 	{ name : 'Longsword', kind : 'weapon' },
// 	{ name : 'Mana potion', kind : 'averagePotion' },
// 	{ name : 'Dead rat', kind : 'junk' },
// 	{ name : 'Endurance potion', kind : 'strongPotion' },
//  { name : 'Healing potion', kind : 'strongPotion' },
// 	{ name : 'Shortbow', kind : 'weapon' }
// ]
$items.countWith(function (item) { return item.kind === 'junk'; })  → Returns 1

If I wanted to count how many of the ‘Healing potions’ were kind ‘strongPotion’ how would I go about doing this? I am also curious if there is a place to learn more about the formatting of the predicate I’m assuming it is a JavaScript thing so I am not sure where I would begin. Thank you!

1 Like

note: the following explanation isn’t 100% technically correct, but it is close enough to get the point across.

Basically the <array>.countWith() method does the following internally:

  • initializes a numerical counter variable to 0 (zero)
  • iterates through the array’s values, passing each one to the anonymous function.
  • increments the counter variable by 1 (one) if the anonymous function returns true.
  • returns the final value of the counter variable.

If the method’s functionally was to be written in SugarCube macro language, it would looks something like…

<<set _matched to 0>>
<<for _item range $items>>
	<<if _item.kind is 'junk'>>
    	<<set _matched += 1>>
    <</if>>
<</for>>

…where the value of _matched is what the method’s returns.

So to change the condition to match the array’s values against, you simply alter the item.kind === 'junk' conditional expression that is being evaluated to produce the anonymous function Boolean return value.

eg. you want to match when the item’s name is 'Healing potion' and its kind is 'strongPotion'. In the above SugarCube based example that would look something like…

<<set _matched to 0>>
<<for _item range $items>>
	<<if _item.name is 'Healing potion' and _item.kind is 'strongPotion'>>
    	<<set _matched += 1>>
    <</if>>
<</for>>

An equivalent $items.countWith() method call would look something like…

$items.countWith(function (item) { return item.name === 'Healing potion' && item.kind === 'strongPotion'; })

I hope all of the above makes it a little easier to understand what the $items.countWith() method is doing internally to return the outcome it does.

note: The arrow function expression base equivalent of the above would be…

$items.countWith((item) => item.name === 'Healing potion' && item.kind === 'strongPotion')
1 Like

Thank you so much! That makes a lot of sense, I appreciate your explanation.