Alphish / gm-community-toolbox

A collection of utility functions for GameMaker.
MIT License
33 stars 6 forks source link

string_to_char_array(str,oneindexed) #47

Closed Alphish closed 1 year ago

Alphish commented 1 year ago

Sometimes, one might want to iterate through a large string, but string_char_at is slow, and the use scenario doesn't quite fit string_foreach.

For these situations, I suggest a string_to_char_array, which will provide an array of strings for faster iteration. I've found that the cost of building the array + the cost of iterating through the array is still far lower than the cost of iterating the string through string_char_at.

The proposed function implementation (with ability to choose between 0-indexed or 1-indexed array, the latter being easier to use together with built-in string functions):

/// @func string_to_char_array(str,[ondeindexed])
/// @desc Creates an array of characters from a given string. It may be 0-indexed or 1-indexed.
/// @arg {String} str           The string to convert to the array.
/// @arg {Bool} [oneindexed]    Whether the first character should start at index of 0 (false) or index of 1 (true).
/// @returns {Array<String>}
function string_to_char_array(_str, _oneindexed = false) {
    var _result = array_create(string_length(_str) + (_oneindexed ? 1 : 0), "");
    var _foreach_context = { result: _result, offset: _oneindexed ? 0 : -1 };
    string_foreach(_str, method(_foreach_context, function(_char, i) {
        result[i + offset] = _char;
    }));
    return _result;
}

And an example code testing performance of string_char_at iteration and array building + iteration:

var _test1 = string_repeat("Hello, world!", 1000);
var _length = string_length(_test1);

// using just string_char_at
var _timer = get_timer();
for (var i = 1; i <= _length; i++) {
    var c = string_char_at(_test1, i);
}
show_debug_message(get_timer() - _timer);

_timer = get_timer();

// converting string to char array, then iterating through the array
var _test2 = string_to_char_array(_test1, true);

for (var i = 1; i <= _length; i++) {
    var c = _test2[i];
}

show_debug_message(get_timer() - _timer);
Alphish commented 1 year ago

Since this feature accumulated enough votes, it can be accepted for development.

Just going to scope out if people are fine with this name or have better ideas. Myself, I took the name inspiration from C#.String.ToCharArray method. In JavaScript you can achieve the same effect by splitting a string by '' literal, which is in line with this feature request. (but we can't change built-in functions)

I also saw string_explode suggested in the aforementioned feature request, but I would rather not use this name because it can be confused with PHP's counterpart of string_split.

With that said:

Alphish commented 1 year ago

Since I don't see any opposition to string_to_char_array (and the functionality got a few upvotes in the first place), the name voting is closed and the feature is accepted for development.

The function should be put into utils_String script, and it should turn a string into an array of its characters. If the oneindexed flag is set to true, the first character should be at the second position of array, at the index of 1 (to match string_char_at, but also other string functions like string_copy that could be used together with the characters array). If the flag is set to 0, the first character should be at the first position of the array, at the index of 0.

The tests should check that a correct array of characters is produced. This includes different 0/1 indexed variations, as well as empty and non-empty strings. Since there are no specific Verrific functions for array comparisons, you may use assert_that(array_equal(...), ...) approach, just like in JSON tests.

The demo might have the user input some string via get_string_async or similar function, and see individual characters from the array in return.

As usual, if you are going to pick this up, please comment here so others are aware. ^^

dicksonlaw583 commented 1 year ago

I'm picking up this ticket as well. I have the implementation, test, and demo ready, pending the acceptance of YoYoGames/GameMaker-Bugs#4255 due to the demo's dependence on the ui_TextArea refactor.

dicksonlaw583 commented 1 year ago

The PR is ready: YoYoGames/GameMaker-Bugs#4253

Alphish commented 1 year ago

Approved and merged.