Hassan Syed
Hassan Syed

Reputation: 20485

Lua iterator to array

In Lua parlance, is there any syntactic sugar for turning an iterator function into an array (repeated invocations with results stored in ascending indices), perhaps something in the standard library ?

I'm tokenizing a string belonging to a protocol and need to to have positional access to elements at the start of the string, and the end of the string is a variant collection.

The code (specific to my use-case) is as follows, I find it hard to believe that it isn't in the standard library :d

local array_tokenise = function (line)
    local i = 1;
    local array = {};

    for item in string.gmatch(line,"%w+") do
      array[i] = item;
      i = i +1
    end

    return array
  end

Upvotes: 10

Views: 8717

Answers (3)

Deco
Deco

Reputation: 5159

As Nicol Bolas said, there is no standard library function that performs the action you desire.

Here is a utility function that extends the table library:

function table.build(build_fn, iterator_fn, state, ...)
    build_fn = (
            build_fn
        or  function(arg)
                return arg
            end
    )
    local res, res_i = {}, 1
    local vars = {...}
    while true do
        vars = {iterator_fn(state, vars[1])}
        if vars[1] == nil then break end
        --build_fn(unpack(vars)) -- see https://web.archive.org/web/20120708033619/http://trac.caspring.org/wiki/LuaPerformance : TEST 3
        res[res_i] = build_fn(vars)
        res_i = res_i+1
    end
    return res
end

Here is some example code demonstrating usage:

require"stringify"

local t1 = {4, 5, 6, {"crazy cake!"}}
local t2 = {a = "x", b = "y", c = "z"}
print(stringify(table.build(nil, pairs(t1))))
print(stringify(table.build(nil, pairs(t2))))
print(stringify(table.build(
        function(arg) -- arg[1] = k, arg[2] = v
            return tostring(arg[1]).." = "..tostring(arg[2])
        end
    ,   pairs(t1)
)))

local poetry = [[
    Roses are red, violets are blue.
    I like trains, and so do you!
    
    By the way, oranges are orange.
    Also! Geez, I almost forgot...
    Lemons are yellow.
]]
print(stringify(table.build(
        function(arg) -- arg[1] == plant, arg[2] == colour
            return (
                    string.upper(string.sub(arg[1], 1, 1))..string.lower(string.sub(arg[1], 2))
                ..  " is "
                ..  string.upper(arg[2]).."!"
            )
        end
    ,   string.gmatch(poetry, "(%a+)s are (%a+)")
)))

Output:

{
    [1] = {
        [1] = 1,
        [2] = 4,
    },
    [2] = {
        [1] = 2,
        [2] = 5,
    },
    [3] = {
        [1] = 3,
        [2] = 6,
    },
    [4] = {
        [1] = 4,
        [2] = {
            [1] = "crazy cake!",
        },
    },
}
{
    [1] = {
        [1] = "a",
        [2] = "x",
    },
    [2] = {
        [1] = "c",
        [2] = "z",
    },
    [3] = {
        [1] = "b",
        [2] = "y",
    },
}
{
    [1] = "1 = 4",
    [2] = "2 = 5",
    [3] = "3 = 6",
    [4] = "4 = table: 00450BE8",
}
{
    [1] = "Rose is RED!",
    [2] = "Violet is BLUE!",
    [3] = "Orange is ORANGE!",
    [4] = "Lemon is YELLOW!",
}

stringify.lua can be found here

Upvotes: 1

GPG
GPG

Reputation: 11

if you are just looking to auto-increment the table key for each data element, you can use table.insert(collection, item) - this will append the item to the collection and sets the key to the collection count + 1

Upvotes: 1

Nicol Bolas
Nicol Bolas

Reputation: 473447

There's no standard library function for it. But really, it's pretty trivial to write:

function BuildArray(...)
  local arr = {}
  for v in ... do
    arr[#arr + 1] = v
  end
  return arr
end

local myArr = BuildArray(<iterator function call>)

This will only work if your iterator function returns single elements. If it returns multiple elements, you'd have to do something different.

Upvotes: 14

Related Questions