Sorting by object key and then printing an array

Twine Version: 2.92
Sugarcube: 2.36

I’ve got help doing this with Harlowe before but I’m having trouble implementing the same thing in Sugarcube.

I’ve got the objects set up thusly in StoryInit

<<set setup.gameItems = {

	"Claw Hammer" : {
		"Name": "Claw Hammer",
	     	"itemType" : "Tool",
                "Price" : 2,
		"Weight": 5
     },   
		
        "Hand Saw": {
                "Name" : "Hand Saw",
        	"itemType" : "Tool",
                "Price" : 5,
		"Weight": 3
	},  
    
        "Nail": {
                "Name" : "Nail",
        	"itemType" : "Tool",
                "Price" : 1,
		"Weight": 1
	},          
        	       
        "Bucket": {
                "Name" : "Bucket",
        	"itemType" : "Tool",
                "Price" : 3,
		"Weight": 4
	}
}>>

<<set $inventory to []>>
<<set $inventory.push("Hand Saw", "Hammer", "Bucket", "Nail")>>

Then I’ve got this in an Inventory passage in the storyMenu which prints out the inventory items and their prices & weights

<<for $i = 0; $i < $inventory.length; $i++>>
       <<set $item = setup.gameItems[$inventory[$i]]>>
        <<print $item.Name>>, Price:  <<print(new Intl.NumberFormat('en-GB', { style: 'currency', currency: 'GBP' }).format($item.Price))>>,Weight: <<print $item.Weight>><br>       
<</for>>

What I’d like is for the player to be able to sort their inventory from lightest at the top to heaviest at the bottom by the weight object key. I’ve been ‘hammering’ away at the similar examples on sorting arrays on the internet but seem to can’t seem to ‘nail’ it.
Many many thanks :slight_smile:

warning: Your example has an error in it, you define a game item with an identifier of "Claw Hammer", but you added an item with an identifier of "Hammer" to the $inventory Array.

The JavaScript Array object has a sort() method, which can be used to sort the elements of an Array in some pre-determined order.

The Description section of that method’s documentation includes an example of how to sort that array using a comparison function that returns either -1, 0, or 1.

note: I suggest you more the initialisation of your setup.gameItems Object to the Story JavaScript area of your project, so it looks like the following…

setup.gameItems = {
	"Claw Hammer" : {
		"Name": "Claw Hammer",
		"itemType" : "Tool",
		"Price" : 2,
		"Weight": 5
     },   
	"Hand Saw": {
		"Name" : "Hand Saw",
		"itemType" : "Tool",
		"Price" : 5,
		"Weight": 3
	},  
    "Nail": {
		"Name" : "Nail",
		"itemType" : "Tool",
		"Price" : 1,
		"Weight": 1
	},          
	"Bucket": {
		"Name" : "Bucket",
		"itemType" : "Tool",
		"Price" : 3,
		"Weight": 4
	}
};

1: Defining the comparison method needed to compare the "Weight" of two items.

For convivence, this would be done on the special setup object, by placing code like the following in the fore-mentioned Story JavaScript area.

/* Compare the Weights of two items */
setup.compareWeights = function (itemA, itemB) {
    if (setup.gameItems[itemA].Weight < setup.gameItems[itemB].Weight) {
        return -1;
    }
    else if (setup.gameItems[itemA].Weight > setup.gameItems[itemB].Weight) {
        return 1;
    }
    return 0;
};

2: Use the new setup.compareWeights() method to sort the elements of the $inventory Array.

<<run $inventory.sort( (itemA, itemB) => setup.compareWeights(itemA, itemB) )>>
2 Likes

That’s awesome, thank you.

I’m a bit confused about the setup.gameItems being in the javascript story section in terms of accessibility.

If I wanted to increase the price of the hammer later on in the story. Could I access that variable and if so what would the sugarcube code look like.
Many thanks as always for sharing your knowledge :pray:

If by “later on in the story” you mean during the Reader/Player’s progress through your project, then I should note the following…

The special setup variable is designed to store Stateless values (and static methods), which is a fancy way of saying values that don’t change after they have been initialised. The content of the setup variable is not part of Progress History, which means it isn’t persisted in Saves.

So if you intend to change the price of “the hammer” during the Reader/Player’s progress through your project, then (at least) that price needs to be somehow stored in a Story Variable so its value will be tracked by Progress History and persisted in Saves.

One way to do this is to have two Objects:

  • a Stateless object for storing the parts of an item definition that don’t change.
  • a Stateful object for storing the parts of an item definition that do change.
setup.items = {
	"Claw Hammer" : {
		"Name": "Claw Hammer",
		"itemType" : "Tool",
		"Price" : 2,
		"Weight": 5
	},
	/* other item definitions...*.
};

State.variables.prices = {
	"Claw Hammer" : 2,
	"Hand Saw": 5,
	"Nail": 1,
	"Bucket": 3
};

When you want to access one of the Stateless properties of the “Claw Hammer” (like its Weight) in a Passage you would reference the setup.items object…

Hammer Weight: <<= setup.items["Claw Hammer"].Weight >>

…and when you want to access that item’s Price you you would reference the $prices Story variable defined earlier…

Hammer Weight: <<= $prices["Claw Hammer"] >>

…and when the price of the hammer needs to be changed at some point in the story, you would use the $prices variable like so…

/* increase hammer's price by 10  */
<<set $prices["Claw Hammer"] += 10>>
1 Like

That’s fantastic. Thank you. Bookmarked!