Deprecated: Function get_magic_quotes_runtime() is deprecated in /home/public/forums/include/common.php on line 79

Deprecated: Function get_magic_quotes_gpc() is deprecated in /home/public/forums/include/common.php on line 83
Foreach [v2.0.3] / Script Submission / GMLscripts.com

GMLscripts.com

Discuss and collaborate on GML scripts
Invert

You are not logged in.

#1 2021-12-16 05:58:17

maras
Member
Registered: 2021-04-25
Posts: 25
Website

Foreach [v2.0.3]

A stackable foreach loop for arrays, lists, maps, structs, grids, strings and number ranges.

This foreach was made using macros so you don't have to pass variables like arguments. You can access them inside of the loop directly.

Reserved keywords: Foreach, Feach, inAarray, inInvArray inList, inInvList, inMap, inStruct, inGrid, inInvGrid, inString, inInvString, inRange, Loop + a global variable FEDATA.

[v2.0.3] Auto init
+ No need for manual initialization anymore
+ Also realized I accidentally placed the loop stuff inside the init function (but it worked so... eh)


[v2.0.2] Speeed
+ Up to 3x faster than the previous version
+ Syntax update - the returned value is now an actual variable instead of being stored in a global struct

Syntax
Feach <var> inData <data> Run

<var> - a variable to use (will overwrite it if already exists)
inData - datatype
<data> - data specified with inData

You can use break and continue

The keyword Loop contains the current loop data like the current index (Loop.i), the current key (Loop.key) or the map function (Loop.map(x))

Note: Feach is just shortened Foreach and you can use the one you prefer

Just in case I ever forget to update it on this forum:
https://github.com/mh-cz/GameMaker-Foreach

Expand/// foreach v2.0.3
//
// Feach <var> inData <data> Run
// 
//     <var> - a variable to use (will overwrite it if already exists)
//     inData - datatype
//     <data> - data specified with inData 
//     
/// GMLscripts.com/license

global.FEDATA = [[], -1];

function _FeArray_(inv, data) constructor {	
	self.data = data;
	self.i = -1;
	self.len = array_length(data);
	self.step = 1;

	if inv {
		i = len;
		step = -step;
	}

	static map = function(v) {
		data[@ i] = v;
	}

	static next = function() {
		i += step;
		return i < len and i > -1;
	}

	static get = function() {
		return data[i];
	}

	static yeet = function() {
		array_pop(global.FEDATA[0]);
		var l = array_length(global.FEDATA[0]);
		if l != 0 Loop = global.FEDATA[0][l-1];
	}
}

function _FeList_(inv, data) constructor {

	self.data = data;
	self.i = -1;
	self.len = ds_list_size(data);
	self.step = 1;

	if inv {
		i = len;
		step = -step;
	}

	static map = function(v) {
		data[| i] = v;
	}

	static next = function() {
		i += step;
		return i < len and i > -1;
	}

	static get = function() {
		return data[| i];
	}

	static yeet = function() {
		array_pop(global.FEDATA[0]);
		var l = array_length(global.FEDATA[0]);
		if l != 0 Loop = global.FEDATA[0][l-1];
	}
}

function _FeMap_(data) constructor {

	self.data = data;
	self.i = -1;
	self.key = "";
	self.keys = ds_map_keys_to_array(data);
	self.len = array_length(keys);
	self.step = 1;

	static map = function(v) {
		data[? key] = v;
	}

	static next = function() {
		i += step;
		return i < len;
	}

	static get = function() {
		key = keys[i];
		return data[? key];
	}

	static yeet = function() {
		array_pop(global.FEDATA[0]);
		var l = array_length(global.FEDATA[0]);
		if l != 0 Loop = global.FEDATA[0][l-1];
	}
}

function _FeStruct_(data) constructor {

	self.data = data;
	self.i = -1;
	self.key = "";
	self.keys = variable_struct_get_names(data);
	self.len = array_length(keys);
	self.step = 1;

	static map = function(v) {
		data[$ key] = v;
	}

	static next = function() {
		i += step;
		return i < len;
	}

	static get = function() {
		key = keys[i];
		return data[$ key];
	}

	static yeet = function() {
		array_pop(global.FEDATA[0]);
		var l = array_length(global.FEDATA[0]);
		if l != 0 Loop = global.FEDATA[0][l-1];
	}
}

function _FeGrid_(inv, data) constructor {

	self.data = data;
	self.xpos = 0;
	self.ypos = 0;
	self.w = ds_grid_width(data);
	self.h = ds_grid_height(data);
	self.i = -1;
	self.len = w * h;
	self.step = 1;

	if inv {
		i = len;
		step = -step;
	}

	static map = function(v) {
		data[# xpos, ypos] = v;
	}

	static next = function() {
		i += step;
		return i < len and i > -1;
	}

	static get = function() {
		xpos = i mod w;
		ypos = i div h;
		return data[# xpos, ypos];
	}

	static yeet = function() {
		array_pop(global.FEDATA[0]);
		var l = array_length(global.FEDATA[0]);
		if l != 0 Loop = global.FEDATA[0][l-1];
	}
}

function _FeRange_() constructor {

	self.from = 0;
	self.to = 1;
	self.step = 1;
	self.i = 0;

	switch(argument_count) {
		case 1:
			to = argument0;
			break;
		case 2:
			from = argument0 - step;
			to = argument1;
			i = from;
			break;
		case 3:
			step = abs(argument2);
			from = argument0 - step;
			to = argument1;
			i = from;
			break;
	}

	static map = function(v) {
		data[# xpos, ypos] = v;
	}

	static next = function() {
		i += step;
		return (from < to and i < to) or (from > to and i > to);
	}

	static get = function() {
		return i;
	}

	static yeet = function() {
		array_pop(global.FEDATA[0]);
		var l = array_length(global.FEDATA[0]);
		if l != 0 Loop = global.FEDATA[0][l-1];
	}
}

function _FeString_(inv, data) constructor {

	self.data = data;
	self.i = -1;
	self.len = string_length(data);
	self.step = 1;

	if inv {
		i = len;
		step = -step;
	}

	static next = function() {
		i += step;
		return i < len and i > -1;
	}

	static get = function() {
		return string_char_at(data, i+1);
	}

	static yeet = function() {
		array_pop(global.FEDATA[0]);
		var l = array_length(global.FEDATA[0]);
		if l != 0 Loop = global.FEDATA[0][l-1];
	}
}

#macro Loop global.FEDATA[1]

#macro Foreach \
	for(var _DataLoadeD_ = false, _CanLooP_ = false; true; { \
	if _DataLoadeD_ { _CanLooP_ = true; if !Loop.next() { Loop.yeet(); break; } var 

#macro Feach \
	for(var _DataLoadeD_ = false, _CanLooP_ = false; true; { \
	if _DataLoadeD_ { _CanLooP_ = true; if !Loop.next() { Loop.yeet(); break; } var 

#macro Run \
	); array_push(global.FEDATA[0], Loop); } \
	}) if _CanLooP_

#macro inArray \
	= Loop.get(); } else { _DataLoadeD_ = true; Loop = new _FeArray_(false, 

#macro inInvArray \
	= Loop.get(); } else { _DataLoadeD_ = true; Loop = new _FeArray_(true, 

#macro inList \
	= Loop.get(); } else { _DataLoadeD_ = true; Loop = new _FeList_(false, 

#macro inInvList \
	= Loop.get(); } else { _DataLoadeD_ = true; Loop = new _FeList_(true, 

#macro inMap \
	= Loop.get(); } else { _DataLoadeD_ = true; Loop = new _FeMap_(

#macro inStruct \
	= Loop.get(); } else { _DataLoadeD_ = true; Loop = new _FeStruct_(

#macro inGrid \
	= Loop.get(); } else { _DataLoadeD_ = true; Loop = new _FeGrid_(false, 

#macro inInvGrid \
	= Loop.get(); } else { _DataLoadeD_ = true; Loop = new _FeGrid_(true, 

#macro inRange \
	= Loop.get(); } else { _DataLoadeD_ = true; Loop = new _FeRange_( 

#macro inString \
	= Loop.get(); } else { _DataLoadeD_ = true; Loop = new _FeString_(false, 

#macro inInvString \
	= Loop.get(); } else { _DataLoadeD_ = true; Loop = new _FeString_(true, 

Examples

Array - return value

Expandvar arr = [1, 2, 3, 4];

Feach v inArray arr Run
	show_debug_message(v);
 
> 1
> 2
> 3
> 4

Array - return index and value

Expandvar arr = ["a","b","c","d"];

Feach v inArray arr Run
	show_debug_message(string(Loop.i) + ", " + string(v));
 
> 0, a
> 1, b
> 2, c
> 3, d

Array - simple map

Expandvar arr = [1, 2, 3, 4];
var multip = 10;

Feach num inArray arr Run
	Loop.map(num * multip);

The array now contains: [10, 20, 30, 40]

List - change some values inside

Expandvar some_values = ds_list_create();
some_values[| 0] = 1; 
some_values[| 1] = 2;
some_values[| 2] = 3; 
some_values[| 3] = 4;

var add = 10;

Feach v inList some_values Run {
	     if Loop.i == 1 Loop.map(-v);
	else if Loop.i == 2 Loop.map(sqr(v));
	else if Loop.i == 3 Loop.map(v + add);
}

// now return them
Feach v inList some_values Run
	show_debug_message(v);

> 1
> -2
> 9
> 14

Grid - store cell coordinate into each cell

Expandvar grd = ds_grid_create(3,3);

Feach v inGrid grd Run
	Loop.map([Loop.xpos, Loop.ypos]);

The grid now contains:
[0,0] [1,0] [2,0]
[0,1] [1,1] [2,1]
[0,2] [1,2] [2,2]

Struct

Expandvar animals = {
	dogs: 10,
	cats: 4,
	rats: 9,
	cows: 7,
	goats: 2,
};

Feach anim_count inStruct animals Run {
	if anim_count < 0 Loop.map(0);
	if Loop.key == "cats" Loop.map(100);
}

Number ranges (returned values written in a single line cuz it's long)

ExpandFeach v inRange 5 Run 
	show_debug_message(v);
	
> 0, 1, 2, 3, 4

Feach v inRange 2, 5 Run 
	show_debug_message(v);
	
> 2, 3, 4

Feach v inRange 2, -2 Run 
	show_debug_message(v);
	
> 2, 1, 0, -1

Feach v inRange 2, -2, 0.5 Run 
	show_debug_message(v);
	
> 2, 1.5, 1, 0.5, 0, -0.5, -1, -1.5

Stackable like regular for loop

ExpandFeach v1 inArray some_arr Run
	Feach v2 inStruct v1 Run
		Feach v3 inRange -v2, v2 Run
			do_something(v1, v2, v3);

One-liner possibilities

Expandif !is_array(some_data) {
	...
}
else Feach v inArray some_data Run {
	...
}

Last edited by maras (2022-05-17 11:50:33)


I'm on the official GM discord > maras#5104

Offline

#2 2022-01-10 16:35:20

xot
Administrator
Registered: 2007-08-18
Posts: 1,239

Re: Foreach [v2.0.3]

Whoa! I'll have to think about this.


Abusing forum power since 1986.

Offline

Board footer

Powered by FluxBB