0 votes
by (170 points)

 

In a passage, I have the following code:

<<set $items=["pizza","sandwich","lasagna"]>>

<<set $food={
	"name": "pasta", 
	"weight":1.0, 
	"description":"just like mom used to make"
}>>


<<set $drink={
	"name":"orange juice",
	"description": "sweet and fresh orange juice",
	"weight":0.5
}>>

<<set $lunchbag={
	"name":"lunchbag",
	"description":"holds several food items and lets you carry them all together",
	"contents":[$food,$drink]
}>>

<<set $items.push($lunchbag)>>

 

I have another passage that displays inventory items by doing the following:

Your inventory:
<<for $i=0;$i<$items.length;$i++>>
	$items[$i]
	
<</for>>

When the array gets to the $lunchbag object, I want it to display the properties of the lunchbag object, and the properties of the $food and $drink objects which are in the 'contents' property array of the lunchbag.

2 Answers

+1 vote
by (23.6k points)
selected by
 
Best answer

 

Just put in a simple <<if>> clause to check whether the element in question has a property called "contents" using def:

<<if def items[_i].contents>>
...
<</if>>

Since in your example your lunchbag is the only element that triggers this condition, you can now put in the properties you want to display. Use a second <<for>> loop to go through the elements within the content property:


<<nobr>>
<<for _i to 0; _i lt $items.length; $_i++>>

<<if def $items[_i].contents>>
  $items[_i].name <br>
  $items[_i].description <br>
  <<for _j to 0; _j lt $items[_i].contents.length; _j++>>
    $items[_i].content[_j].name <br>
    $items[_i].content[_j].description <br>
    $items[_i].content[_j].weight
  <</for>>
<<else>>
 $items[_i]
<</if>>
<br><br>
<</nobr>>

<</for>>

 

by (170 points)
Thank you. This would work for my current example, but what if there were more objects like the $lunchbag in $items? And what if their properties are not "name", "description", and "weight" and have varying properties?
by (159k points)

There are JavaScript functions that can be used to learn what keys / properties an object has, like the Object.keys() function. However it is not guaranteed that the order of the keys/properties within the returned Array will always be in the same order that you originally defined them.

eg. you defined the $food and $drink objects like so.

<<set $food = {
	"name": "pasta", 
	"weight": 1.0, 
	"description": "just like mom used to make"
}>>

<<set $drink = {
	"name": "orange juice",
	"description": "sweet and fresh orange juice",
	"weight": 0.5
}>>

And it makes sense that when you display both those object's key values that you want them listed in some logical order, like: name, description, weight.

But the Object.keys($food) and Object.keys($drink) functions calls will return their associated Array of keys in whatever order they like, they may be in the same order that the keys of the associated object were define in and they may not.

This is why the code of both idling and my solutions controls the order that the key values are outputed in.

 

by (170 points)

Thank you for the explanation. I still don't understand a few parts of your code, like how you did it without any loops.

2. What is 

setup.listItems

that you assign the anonymous function to?

3.What does 

Array.isArray(list)

do?

4.What does 

list.forEach()

do and why is there an anonymous function in there?

by (159k points)

SugarCube includes a setup object which you can define your own custom JavaScript properties (variables and functions) on, the object has a 'global' like scope.

eg. it and any properties you define on it are accessable within most areas of a SugarCube based story.

The JavaScript Array.isArray() function is used to determine if an object is an Array or not.

The JavaScript <Array>.forEach() function can be used to execute a block of code against each element within an Array, the anonymous function is one means to supply the bock of code.

+1 vote
by (159k points)
edited by

Add the following custom JavaScript function to your project's Story JavaScript area. It processes the $item Array you pass it to generate a Unorder List (<ul>) based String which you can use a <<print>> macro to display.

setup.listItems = function (list) {
	var output = "";
	if (Array.isArray(list)) {
		output += '<ul class="inventory">';
		list.forEach(function (item) {
			if (typeof item === "string") {
				output += '<li>' + item + '</li>';
			}
			else {
				var keys = Object.keys(item);
				output += '<li>';
				if (keys.includes('name')) {
					output += item.name;
				}
				if (keys.includesAny(['description', 'weight', 'contents'])) {
					output += '<ul>';
					if (keys.includes('description')) {
						output += '<li>desc: ' + item.description + '</li>';
					}
					if (keys.includes('weight')) {
						output += '<li>weight: ' + item.weight.toFixed(1) + '</li>';
					}
					if (keys.includes('contents')) {
						output += '<li>contents:' + setup.listItems(item.contents) + '</li>';
					}
					output += '</ul>';
				}
				output += '</li>';
			}
		});
		output += '</ul>';
	}
	return output;
}

note: The above uses recursion internally to handle the use-case where items can contain other items, which in turn can contain other items etc...

You use the above setup.listItems() function like so

Your inventory:\
<<= setup.listItems($items)>>


P.S. The above produces a HTML structure like the following...

Your inventory:\
<ul class="inventory">
  <li>pizza</li>
  <li>sandwich</li>
  <li>lasagna</li>
  <li>lunchbag
    <ul>
      <li>desc: holds several food items and lets you carry them all together</li>
      <li>contents:
        <ul class="inventory">
          <li>pasta
            <ul>
              <li>desc: just like mom used to make</li>
              <li>weight: 1.0</li>
            </ul>
          </li>
          <li>orange juice
            <ul>
              <li>desc: sweet and fresh orange juice</li>
              <li>weight: 0.5</li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

... which you can style using CSS like the following within your project's Story Stylesheet area.

ul.inventory {
	list-style: none;
	margin-block-start: 0;
	margin-left: 0;
}

 

by (170 points)
Thanks, but I don't understand half of the javascript code lol
by (44.7k points)

The main thing you need to understand in the above JavaScript code is that the Object.keys(objectVariable) method will return an array of the property names on the "objectVariable" you pass to it.  This allows that code to display a list of all properties and their values on an object.

Beyond that, Greyelf has made it pretty much copy and paste for you.  If you have any specific questions, feel free to ask and I'm sure someone can help give you an answer.

...