lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Greg Bakker wrote:
Hi,

I'm curious as to how best to create a table via a direct mapping from another.

For example, say you want

{ 1, 2, 3 } => { {name="something1", value=1}, {name="something2", value=2}, {name="something3", value=3} }

Currently I'm using

    function map(f, v, ...)
      if v then
        return f(v), map(f, ...)
      end
    end

    function func(v) return {name="something"..v, value=v} end

    a = { 1, 2, 3 }
    b = { map(func, unpack(a)) }

which is probable stack abuse. Is there a canonical approach; maybe a better way is to use a loop and insert-at-end?

Thanks,
Greg Bakker

My preference is that map transforms an iterator into an iterator rather than a table into a table.

Simplified implementation (better code is part of a Gem I'm writing):

Note: f,o,s is an iterator triple (nextfunc, object, state); i.e. the triple provided to a 'for' statement.

function map(func, f, o, s)
  local function aux(k, ...)
    if k ~= nil then
      return k, func(...)
    end
  end
  local function nexter(o, s)
    return aux(f(o, s))
  end
  return nexter, o, s
end

This 'map' is called with a function and an iterator, and
returns an iterator.

function func(v) return {name = "something"..v, value = v} end
local a = {10, 20, 30}
local b = {}
for k, v in map(func, ipairs(a)) do b[k] = v end

Note that this implementation of map is composable:

function inflate(v) return {name = v.name, value = v.value * 1.05} end

for k, v in map(inflate, map(func, ipairs(a)) do ... end

Often, we actually want a fold (collector):

function collect(t, f, o, s)
  for k, v in f, o, s do t[k] = v end
  return t
end

So that we can then write:

b = collect({}, map(inflate, map(func, ipairs(a))))

The nice thing is that this will work with any iterator, not just ipairs.