Object's property has multiple entries. How to get length or access each in a for loop? (sugarcube 2.30)

so I have something like this…

<<set $char={
stats:{
person1: { name: “Bob”, nickname: “Robert” },
person2: { name: “Steve”, nickname: [“Harry”, “Ralph”] }
}
}>>

The following code prints the nicknames to the screen…

<<set _stats to Object.keys($char.stats)>>
<<for _i = 0; _i < _stats.length; _i++>>
<<print $char.stats[_stats[_i]].nickname>>
<>

As you can see person2 has multiple nicknames. His nickname returns as Harry, Ralph. I want to be able to check for these individually. Is there a way to determine length of the nickname property so I can run through a for loop. Or a way to push each individual nickname to another array and get length that way. What’s the code to access the nickname separately so I can compare if a person3 for instance shares a similar nickname. Thanks.

You should just be able to ask for .nickname.length on whatever object (e.g. $char.stats[_stats[_i]].nickname.length).

But if it’s a string that will give you the length of the string, so…you’ll probably first want to check if typeof obj.nickname === 'string'. Or store even single nicknames in an array: nickname: ["Robert"].

Ok, so <<print $char.stats[_stats[_i]].nickname.length>> does return the correct number as long as I have brackets [ ] around the nicknames…you were right in that it returned the string length when [ ] weren’t involved.

What I’m really after though is how do I access the 2nd nickname only if someone has a second nickname for instance.

I’ve tried something like <<print $char.stats[_stats[_i]].nickname.[1]>>, but that just errors.

Thanks

Doh! Just noticed the “.” between the nickname and [1]. I removed that and I’m getting what i want now. thanks!

If you’re converting the object to an array of keys, you might as well sort the output:

<<set _stats to Object.keys($char.stats).sort()>>

However, if you aren’t sorting it, you could do the <<for>> loop like this:

<<for _data range $char.stats>>\
	Name: <<= _data.name>>
	<<if Array.isArray(_data.nickname)>>\
		<<for _nick range _data.nickname>>\
			Nickname: _nick
		<</for>>\
	<<else>>\
		Nickname: <<= _data.nickname>>
	<</if>>\
<</for>>

That will print out each name, along with their nickname(s). See the range version of the <<for>> macro.
 

You can use Array.isArray() to determine whether a value is an array or not. If it’s an array you can use the .length property on the value to determine the number of nicknames in that value.
 

Very close. You’d do that like this:

<<print $char.stats[_stats[_i]].nickname[1]>>

and that will get you the second nickname from that array.
 

There’s a problem with “similar”. Code is generally good at “the same” and bad at “similar”, unless you can exactly describe what “similar” means in code.

You could detect an exact match among nicknames like this:

<<if $char.stats[_stats[0]].nickname.includesAny($char.stats[_stats[1]].nickname)>>
	Characters have identical nicknames.
<<else>>
	No matching nicknames for these characters.
<</if>>

That uses the .includesAny() method to tell you if there’s an exact match, but not which nickname matches.

Hope that helps! :slight_smile:

P.S. You should mark any code using the “code block” </> button. That will prevent this forum from eating macros.

Thank you all, very helpful.