lua-users home
lua-l archive

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


Hi, list!

I need the function which would work as follows:

print(
    extract_keys(
        { a = 1, b = 2, c = 3 },
        "a", "b", "xxx", "c", "yyy"
      )
  )
 --> 1        2        nil         3        nil

It is quite simple to write one in C API:

    static int Lextract_keys(lua_State * L) {
      int nargs = lua_gettop(L);
      int i = 2;

      luaL_checktype(L, 1, LUA_TTABLE);
      for (i = 2; i <= nargs; ++i) {
        lua_pushvalue(L, i);
        lua_gettable(L, 1);
      }

      return nargs - 1;
    }

However, I'd like to have one in plain Lua. Naïve version follows:

    function extract_keys(t, ...)
      local results = {}
      local nargs = select("#", ...)
      for i = 1, nargs do
        results[i] = t[select(i, ...)]
      end
      return unpack(results, 1, nargs) -- Explicitly specifying range
to handle holes in results array
    end

It is not quite good enough because it does create an extra table. Is
there a better way to implement it except to generate code as follows?

    do
      local function generate_extractor(n)
        assert(type(n) == "number")
        local a, r = {}, {}
        for i = 1, n do
          a[i], r[i] = 'a'..i, 't[a'..i..']'
        end
        return assert(
            loadstring(
                [[return function(t,]]..table.concat(a,
",")..[[)return ]]..table.concat(r, ",").."end",
                "extract_keys_"..n
              )
          )()
      end

      local extractors = setmetatable(
          {
            [0] = function(t) end;
            [1] = function(t, a1) return t[a1] end;
            [2] = function(t, a1, a2) return t[a1], t[a2] end;
            [3] = function(t, a1, a2, a3) return t[a1], t[a2], t[a3] end;
            [4] = function(t, a1, a2, a3, a4) return t[a1], t[a2],
t[a3], t[a4] end;
            [5] = function(t, a1, a2, a3, a4, a5) return t[a1], t[a2],
t[a3], t[a4], t[a5] end;
          },
          {
            __index = function(t, k)
              local v = generate_extractor(k)
              rawset(t, k, v)
              return v
            end
          }
        )

      function extract_keys(t, ...)
        return extractors[select("#", ...)](t, ...)
      end
    end

Assuming that we're extremely rarely need to use that __index stub,
and we always can add more hand-written default entries.

TIA,
Alexander.