How to find generic object in array and return something

Hello.

I’m creating a card game. I have a problem with sorting cards in the deck view passage.

At the beginning I have some generic objects stored in the array.

<<set $s7 to {type: "s", num: 7, id: 107}>>
<<set $s8 to {type: "s", num: 8, id: 108}>>
<<set $s9 to {type: "s", num: 9, id: 109}>>
<<set $s10 to {type: "s", num: 10, id: 110}>>
<<set $deck to [$s7, $s8, $s9, $s10]>>

And some cards to find during the game

\<<set $a7 to {type: "a", num: 7, id:207}>>
\<<set $a8 to {type: "a", num: 8, id:208}>>
\<<set $a9 to {type: "a", num: 9, id:209}>>
\<<set $a10 to {type: "a", num: 10, id:210}>>

Then the player finds some new cards and puts them into the deck in random order

<<set $deck.push($a8)>>
<<set $deck.push($a10)>>
<<set $deck.push($a7)>>
<<set $deck.push($a9)>>

So our array looks like that now

[$s7, $s8, $s9, $s10, $a8, $a10, $a7, $a9]

I want to show him all the cards that he’s already found. Not in random order, but sorted properly. And I dont know how to do that.
I tried this

<<if $deck.count($s7) gt 0>>
<img @src='"img/cards/"+ $s7.type + $s7.num + ".svg"' style="position:relative; left:0; top:0;" height="`10%" width="10%">
<</if>>
<<if $deck.count($a7) gt 0>>
<img @src='"img/cards/"+ $a7.type + $a7.num + ".svg"' style="position:relative; left:0; top:0;" height="`10%" width="10%">
<</if>>
/* etc. */

But .count method always returns 0, because it cant read generic object inside the array. Maybe I’m doing something wrong in this solution?

Or maybe there is a way to sort generic objects inside the array. If I can make array to look like that, it would be awesome

[$s7, $a7, $s8, $a8, $s9, $a9, $s10, $a10]
1 Like

When working with arrays we want the .length property not .count (not sure that’s a valid sugarcube/javascript property).

To get your array sorted by the deck’s .num property you can use the sort method. This will compare all the elements in the array and sort ascending by .num

<<set $deck.sort((a,b) => {
return a.num - b.num})>>

Then you can loop through the array with a for loop. This will loop 8 times (equal to $decks length). During each loop through we grab the current type and num property based on the arrays current index value, _i ,and concatenate it with the image path you had

<<for _i=0;_i < $deck.length;_i++>>
<img @src='"img/cards/"+ $deck[_i].type + $deck[_i].num + ".svg"' style="position:relative; left:0; top:0;" height="`10%" width="10%">
<</for>>
1 Like

Omg! Thanks, your Majesty! It works just like magic! :star_struck:

What Laton was attempting had nothing to do with .length.

SEE: The <Array>.count() method.

1 Like

Yep. My idea was to find how many times object appears in the array. And if it appears once - count() method returns 1. If nothing there, returns 0. I’ve tested that, it works with numbers. But with generic objects it doesn’t work.

Anyway, jeb019’s solution works just fine =) And now I understand how <<for>> works :grinning:

It can read objects just fine. The problem is that you want it to test for equivalence when it tests for equality—i.e., you’re asking it to do something it doesn’t. You could use the <Array>.countWith() method to do as you were attempting since you can specify your own comparison predicate.

Having said that, you’d be much better off sorting the list into the proper order and then printing that. Both less work on your part and less room for error.

Ordering

If you wanted the deck ordered simply by card number, then jeb019’s method will work just fine.

If you wanted the deck ordered by both card type and number, then you’ll need something a bit more involved.

Example - Order by type and number

If the card’s id properties are as they seem, a combination of type and number, then they can be used as the sorting key.

Place the following into your Story JavaScript:

setup.getTypeId = function getTypeId(id) {
	return Math.trunc(id / 100);
};

setup.orderByTypeAndNumber = function orderByTypeAndNumber(a, b) {
	const aTypeId = setup.getTypeId(a.id);
	const bTypeId = setup.getTypeId(b.id);

	/* Check the types. */
	if (aTypeId > bTypeId) {
		return 1;
	}
	else if (aTypeId < bTypeId) {
		return -1;
	}

	/* If the types are equal, check the numbers. */
	if (a.num > b.num) {
		return 1;
	}
	else if (a.num < b.num) {
		return -1;
	}

	/* If the types and numbers are equal, the cards are equivalent. */
	return 0;
};

Then where you want to print the sorted list:

<<run $deck.sort(setup.orderByTypeAndNumber)>>
<<for _card range $deck>>
    <img @src="'img/cards/' + _card.type + _card.num + '.svg'" style="position:relative; left:0; top:0;" height="10%" width="10%">
<</for>>

NOTE: The above will cause $deck itself to be sorted into the new order. If you need to keep it in its original order, then you’ll need to make a copy of it to sort for printing.

You can do so by making the following changes to the above examole.

Changes - Copy & sort

Find:

<<run $deck.sort(setup.orderByTypeAndNumber)>>

Replace with:

<<set _orderedDeck to clone($deck).sort(setup.orderByTypeAndNumber)>>

Find:

<<for _card range $deck>>

Replace with:

<<for _card range _orderedDeck>>
1 Like

Thanks =) It’s more complicated, but more options here. And I think I will use your information for inventory system. Because items will have more properties, than cards. I did sorting just with my hands :upside_down_face:, and now I can automate it, thanks to you =)

Also, thanks for clone. I was looking for deep copy for some time :upside_down_face: