How to scan all elements of an array, starting with the second, in a unique `for / ipairs`?

I have an array with 3 elements:

arr = {11, 22, 33}

I want to print all 3 elements, but starting with the 2nd, so the result might be:

22
33
11

Apparently I should have a second for for this.

Any way to do this in a single for / ipairs ?

table.sort(arr, function(a, b)
    return a > b
end)

arr[1], arr[2] = arr[2], arr[1]

no need for a for loop

1 Like

That goes 2, 1, 3, not 2, 3, 1.

2 Likes

image

mm no

Another solution would be inserting the first element back into the array and removing it:

local Array = {11, 22, 33};

table.insert(Array, Array[1]);
table.remove(Array, 1);

for i = 1, #Array do
    print(Array[i]);
end

Here’s another solution, similar to ReturnedTrue’s, except it never removes the first element:

local YOUR_TABLE = {11, 22, 33}
local inext = ipairs{}

local function handleIndex(i, v)
	--[[whatever would be in ur loop]]
	print(i, v)
end

for i, v in inext, YOUR_TABLE, 1 do
	handleIndex(i, v)
end

handleIndex(inext(YOUR_TABLE, 0))

This is based on how ipairs is implemented. It returns a function that takes a table, t, and an index, i, and finds the next index-value pair given that it’s an array. It’s similar to pairs and its function next, which is why I called it inext.

2 Likes

Sorry, i should have been more specific.
The array is not limited to just 3 elements, it can have n elements and I can start with any element other than the first element.
It is a loop that must scan all the elements of the array, regardless of where it starts.
I know I should create a second one for this, but my question is whether I could do this more cleanly.

1 Like

Could you show an example of your desired result with a longer array.

Thanks for everyone’s ideas. My question was whether I could “save code” to loop between elements of an array, but apparently there is no ready way to do this. In this way I created a solution:

arr = {5, 3, 8, 1, 7, 9, 2} 

I want to start with the 3rd element, for example, so it might print:

8, 1, 7, 9, 2, 5, 3

I made this function:

local arr = {5, 3, 8, 1, 7, 9, 2}

local function loopArray(array, starElement)
	local startFound
	for _, v in ipairs(array) do
		if v == starElement then startFound = true end
		if startFound then print(v) end
	end
	for _, v in ipairs(array) do
		if v == starElement then break end
		print(v)
	end
end

loopArray(arr, 8) 

Will print:

8
1
7
9
2
5
3

This might be doable with a custom iterator, but it’d get ugly fast. You’re better off with two loops. Quick sample of how bad a solution would look:

local function xpairs(t, start)
	local flip = false
	return function(t, ind)
		local i, v = ind + 1, t[ind + 1]

		if v == nil then
			i, v = i - #t, t[i - #t]
		elseif i == start then
			if flip then return end
			flip = not flip
		end

		return i, v
	end, t, start - 1
end

local YOUR_TABLE = {11, 22, 33, 44, 55, 66, 77, 88, 99}

for i, v in xpairs(YOUR_TABLE, 4) do
	print(i, v)
end

I’d highly advise against using this in production, but here’s the general idea of how it works:

In Lua, for args in f, ... calls a function with .... It keeps getting called with new args based on what it returns. The code in the do block with be run, with args set to whatever f returns. pairs and ipairs are what we call iterator factories, which means they return an iterator with the relevant args.

We can define our own factory, xpairs, which will take args that the internal iterator function relies on (like start), and implement behavior to wrap around when it reaches the end, only stopping (done by returning nil) when it reaches the start again.

(flip is just to allow it to loop past start the first time and stop the loop on the second time around.)

2 Likes

or this (if you want to use an index… I noticed your solution searches for a value in the list instead. Don’t forget to handle case of value not found)

local function LoopArray(arr, start)
    if start == nil then start = 1 end   -- make start arg optional

    -- type checking
    assert(type(arr)=="table", "First arg must be a table")
    assert(type(start)=="number", "Start value is not a number") 

    -- wrap index using modulo
	for i=0, #arr-1 do
		print( arr[(i+start-1) % #arr+1] )
	end
end 

This wraps the index around using modulo. It protects against (allows for) start values that are bigger than the array size, but note that a start value of 0 will begin printing on the last entry since 1 is the first.

The above is basically equiv to:

    start = (start-1)%#arr+1   -- force start value to be within arr length
	local index = nil
	for i=1, #arr do
		index = i+start-1
		if index > #arr then
			index = index - #arr
		end
		print( arr[index] )
	end
2 Likes