• Hello [name]! Thanks for joining the GMC. Before making any posts in the Tech Support forum, can we suggest you read the forum rules? These are simple guidelines that we ask you to follow so that you can get the best help possible for your issue.

Discussion Universal Getter + Setter: Workaround for nested accessors

FrostyCat

Member
Universal Getter + Setter (GMS 2.x)

Overview
This GML extension adds a functional equivalent of the chained accessor feature on the roadmap, allowing you to reach into deeply nested arrays and data structures in one line. It also adds the ability to use negative index numbers to count backwards from the end on arrays, lists and grids, similar to Python and Ruby.

Downloads
YoYo Marketplace: Link
GitHub: Download | Repository

Examples
Code:
var json = @'{ "a": [1, 2, { "b": 3 }]}',
   json_data = json_decode(json);
show_message(Get(json_data, "a", 2, "b")); //3
show_message(Get(json_data, "a", -2)); //2
Code:
var nested_array = [[1, 1], [4, 4], [9, 9]];
Set(nested_array, -1, 1, 16);
show_message(Get(nested_array, 2, 0)); //9
show_message(Get(nested_array, 2, 1)); //16
 
Last edited:

YellowAfterlife

ᴏɴʟɪɴᴇ ᴍᴜʟᴛɪᴘʟᴀʏᴇʀ
Forum Staff
Moderator
This is nice, though could be further improved - I think a good amount of time is spent on string alloc/dealloc/concat here

I took a shot at tweaking the getter,
  • Instead of casting index to real and concatenating typeofs, indexes are assumed to be numeric if they are not arrays/strings (you get an error on non-numeric indexing anyway)
  • Instead of constructing stack_top & co. and switching on their first item, they are constructed on startup, macros are pointing to global variables containing them, and there's a switch on array reference (which is OK in VM, YYC, and JS all alike). This also eliminates oddities if an index is an arbitrary 1-element array due to user error.
  • Since Pos is only used with these scripts, it's items are tagged and pooled.
    This is 20% slower on VM but ~25% faster on YYC.
    More importantly though, it allows to eliminate unobvious behaviour if index happens to be an unrelated 2-item array per chance.
This results in 2x..4x performance boost (see attachments)
Code:
/// @param array_or_ds
/// @param ...indexes
var r = argument[0];
for (var i = 1; i < argument_count; ++i) {
    var k = argument[i];
    if (is_array(k)) {
        if (array_length_1d(k) > 0) {
            var k0, k1;
            if (k[0] == global.g_ypos_tag) {
                k0 = k[1];
                k1 = k[2];
                ds_stack_push(global.g_ypos_pool, k);
            } else {
                k0 = k[0];
                k1 = k[1];
                // not showing an error because we're comparing non-pooled
                //show_error("Invalid index `" + string(k) + "` [" + string(i) + "]", 1);
            }
            if (is_array(r)) {
                if (k0 < 0) k0 += array_height_2d(r);
                if (k1 < 0) k1 += array_length_2d(r, k0);
                r = r[k0, k1];
            } else {
                if (k0 < 0) k0 += ds_grid_width(r);
                if (k1 < 0) k1 += ds_grid_height(r);
                r = r[#k0, k1];
            }
        } else {
            if (is_array(r)) {
                show_error("Invalid index `" + string(k) + "` [" + string(i) + "]", 1);
            } else switch (k) {
                case ystack_top: r = ds_stack_top(r); break;
                case yqueue_head: r = ds_queue_head(r); break;
                case ypq_min: r = ds_priority_find_min(r); break;
                case ypq_max: r = ds_priority_find_max(r); break;
                default: show_error("Invalid index `" + string(k) + "` [" + string(i) + "]", 1);
            }
        }
    } else if (is_string(k)) {
        r = r[?k];
    } else {
        if (is_array(r)) {
            if (k < 0) k += array_length_1d(r);
            r = r[k];
        } else {
            if (k < 0) k += ds_list_size(r);
            r = r[|k];
        }
    }
}
return r;
Project and tester: https://www.dropbox.com/s/kaew87g89imumcw/yget.yyz?dl=0

Can make it fit the original format and assemble a PR if you'd like.
 

Attachments

immortalx

Member
I'd normally say sorry to bump this thread, but I'm not sorry because this is truly a lifesaver.
I was pointed here by YellowAfterLife after a discussion in this thread and I initially couldn't import the extension.
When I tried to import UniversalGetSet.yy from the releases page, i got an error popup:
Code:
The external project file cannot be read to import this resource
Today I gave it another shot by trying to import the same file from the source and it worked!

I don't know what could be the problem, so I thought I'd let you know.

In my project I have arrays stored inside a ds_list and I previously had to do this:
Code:
var arr = ds_list_find_value(t,6);
arr[5] = false;
t[| 6] = arr;
and now it's as simple as this:
Code:
Set(t,6,5,false)
Really, really powerful. Thank you very much!
 
Top