10

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

4 Answers 4

14

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.

Sign up to request clarification or add additional context in comments.

10 Comments

The important line could also be written as table.insert(arr, v), which may be slightly more readable depending on your preferences. Not sure about performance implications though.
@KevinBallard, it would hurt performance very badly. In fact, the most efficient way to do this would be by incrementing an index variable i-- still one addition operation per iteration, but no # operation.
@KevinBallard trac.caspring.org/wiki/LuaPerformance a[#a+1] = x executes in 223% of the time as a[i] = x; i = i+1 (0.453 vs 0.203 seconds for a million iterations.) For comparison, table.insert (a, x) executes in 1.25 seconds, which is a major performance hit.
@MaxE. Huh, color me surprised. I would have expected it to be a bit better-optimized than that. Though you're not quite right, in my tests using table.insert() is only slightly slower than using a[#a+1] (250ms for table.insert() vs 200ms for a[#a+1]).
@KevinBallard My results, using 1 million iterations, Lua 5.1.4, on Linux with a Core i5 at 2.53 ghz: pastebin.com/QSu099ri At 10 million iterations: pastebin.com/yMbABu5m At 50 million iterations: pastebin.com/Vw9ivVXW Which seems to confirm your results.
|
1

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

Comments

1

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

Comments

0

Unfortunately, there isn't a builtin that does this for us like table.unpack or table.pack, BUT there is a way to do this using a custom function!

Steps for building a Lua table out of an iterator:

  1. Initialise result table (in my case, b)
  2. Loop through the iterator's values
function buildTbl(a)
    local b={}
    for c in(a)do(table.insert)(b,c)end
    return(b)
end

(i dont know how to make my writing look less ai)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.