【问题标题】:Table Filter in Lua for multidimensional tablesLua中的表过滤器用于多维表
【发布时间】:2018-09-19 20:23:56
【问题描述】:

我正在设计一个通用表过滤器,它可以从给定表中删除条目,问题是键不是唯一的来完成此操作,并且类型也不同。

让我用一个例子更清楚地说明

SampleTable = {
    { key1 = 10, key2 = 'name_1', Str = 'sample_string_1'     },
    { key1 = 10, key2 = 'name_3', Str = 'sample_string_2'     },
    { key1 = 11, key2 = 'name_2', Mac = {'ID1', 'ID2', 'ID3'} },
    { key1 = 11, key2 = 'name_2', Mac = {'ID1'}               },
    { key1 = 12, key2 = 'name_4', Mac = {'ID2', 'ID3'}        }
}

function filter(inputTable, ...) 
    filterItems = {...}
end

我想传递任意数量的键来过滤这个表

local key1 = 11
local Mac = 'ID1'
filter(SampleTable, key1, Mac)
 -- Should return -> { key1 = 11, key2 = 'name_2', Mac = 'ID1'},


key1 = 12
Mac = 'ID3'
filter(SampleTable, key1, Mac)
-- Should return -> { key1 = 12, key2 = 'name_4', Mac = ID3'}

key1 = 11
Mac = 'ID2'
filter(SampleTable, key1, Mac)
 -- Should return both    
-- { key1 = 11, key2 = 'name_2', Mac = ID2'},
-- { key1 = 11, key2 = 'name_5', Mac = ID2'},

key1 = 10
Str = 'sample_string_2'
filter(SampleTable, key1, Str)
 -- Should return { key1 = 10, key2 = 'name_3', Str = 'sample_string_2'}

我目前的解决方案是搜索两个表中的每个键值对

function filter(tIn, tFilter) 
    local retain = true
    local exist  = nil
    local tOut = tIn
    local _findInTable = function (t, k, v)
        if(not t[k]) then return true
        elseif(t[k] and t[k] == v) then return true
        else return false end
    end

    for i, t in ipairs (tIn) do
        for k,v in pairs (tFilter) do
            exist = _findInTable(t, k, v)
            retain = retain and exist
        end
        if not retain then tOut[i] = nil end
        retain = true
    end
    return tOut
end

local myTable = filter(SampleTable, {key1 = 11, Mac = 'ID1'})

问题是我无法预见递归将如何提供帮助。 这段代码在我有以下 SampleTable 时有效,正如您所见,Mac 不是我的代码的子表。

SampleTable = {
    { key1 = 10, key2 = 'name_1', Str = 'sample_string_1'     },
    { key1 = 10, key2 = 'name_3', Str = 'sample_string_2'     },
    { key1 = 11, key2 = 'name_2', Mac = 'ID1'                 }
    -- { key1 = 11, key2 = 'name_2', Mac = {'ID1', 'ID2', 'ID3'} },
    -- { key1 = 11, key2 = 'name_2', Mac = {'ID1'}               },
    -- { key1 = 12, key2 = 'name_4', Mac = {'ID2', 'ID3'}        }
}

【问题讨论】:

  • 您是否尝试按名称查看每个键?如果您想以不同方式处理不同的键,那将是有意义的。
  • 这是一般问题和对功能的模糊描述。您尝试了什么,什么没有奏效?
  • 我目前的解决方案,希望有更好的描述。我一直在寻找递归解决方案。

标签: lua lua-table


【解决方案1】:

从您的问题来看,尚不完全清楚您是在处理递归模式(任意深度和分支结构),还是提供的示例是否就是它的全部(键始终分配给 n 值,模式中没有递归)。没有更复杂的示例,我决定在更简单的情况下实现它。


这是我对您的问题的解决方案,包括您的示例:

local sample = {
    { key1 = 10, key2 = 'name_1', Str = 'sample_string_1'     },
    { key1 = 10, key2 = 'name_3', Str = 'sample_string_2'     },
    { key1 = 11, key2 = 'name_2', Mac = {'ID1', 'ID2', 'ID3'} },
    { key1 = 11, key2 = 'name_2', Mac = {'ID1'}               },
    { key1 = 12, key2 = 'name_4', Mac = {'ID2', 'ID3'}        }
}

--- Check if a row matches the specified key constraints.
-- @param row The row to check
-- @param key_constraints The key constraints to apply
-- @return A boolean result
local function filter_row(row, key_constraints)
    -- Loop through all constraints
    for k, v in pairs(key_constraints) do
        if v and not row[k] then
            -- The row is missing the key entirely,
            -- definitely not a match
            return false
        end

        -- Wrap the key and constraint values in arrays,
        -- if they're not arrays already (so we can loop through them)
        local actual_values = type(row[k]) == "table" and row[k] or {row[k]}
        local required_values = type(v) == "table" and v or {v}

        -- Loop through the values we *need* to find
        for i = 1, #required_values do
            local found
            -- Loop through the values actually present
            for j = 1, #actual_values do
                if actual_values[j] == required_values[i] then
                    -- This object has the required value somewhere in the key,
                    -- no need to look any farther
                    found = true
                    break
                end
            end

            if not found then
                return false
            end
        end
    end

    return true
end

--- Filter an array, returning entries matching `key_values`.
-- @param input The array to process
-- @param key_values A table of keys mapped to their viable values
-- @return An array of matches
local function filter(input, key_values)
    local result = {}

    for i = 1, #input do
        local row = input[i]
        if filter_row(row, key_values) then
            result[#result + 1] = row
        end
    end

    return result
end

这是示例输出,带有实用程序 deep_print() 函数:

--- Recursively print out a Lua value.
-- @param value The value to print
-- @param indent Indentation level (defaults to 0)
-- @param no_newline If true, won't print a newline at the end
local function deep_print(value, indent, no_newline)
    indent = indent or 0

    if type(value) == "table" then
        print("{")
        for k, v in pairs(value) do
            io.write(string.rep(" ", indent + 2) .. "[")
            deep_print(k, indent + 2, true)
            io.write("] = ")
            deep_print(v, indent + 2, true)
            print(";")
        end
        io.write(string.rep(" ", indent) .. "}")
    elseif type(value) == "string" then
        io.write(("%q"):format(value))
    else
        io.write(tostring(value))
    end

    if not no_newline then
        print()
    end
end

-- The following is a mix of Lua code
-- and the script's output

deep_print(filter(sample, {key1 = 10}))
-- outputs
{
  [1] = {
    ["key1"] = 10;
    ["key2"] = "name_1";
    ["Str"] = "sample_string_1";
  };
  [2] = {
    ["key1"] = 10;
    ["key2"] = "name_3";
    ["Str"] = "sample_string_2";
  };
}

deep_print(filter(sample, {key2 = "name_4"}))
-- outputs
{
  [1] = {
    ["key1"] = 12;
    ["key2"] = "name_4";
    ["Mac"] = {
      [1] = "ID2";
      [2] = "ID3";
    };
  };
}

deep_print(filter(sample, {Mac = {"ID2", "ID3"}}))
-- outputs
{
  [1] = {
    ["key1"] = 11;
    ["key2"] = "name_2";
    ["Mac"] = {
      [1] = "ID1";
      [2] = "ID2";
      [3] = "ID3";
    };
  };
  [2] = {
    ["key1"] = 12;
    ["key2"] = "name_4";
    ["Mac"] = {
      [1] = "ID2";
      [2] = "ID3";
    };
  };
}

deep_print(filter(sample, {Mac = {"ID2"}}))
-- also outputs
{
  [1] = {
    ["key1"] = 11;
    ["key2"] = "name_2";
    ["Mac"] = {
      [1] = "ID1";
      [2] = "ID2";
      [3] = "ID3";
    };
  };
  [2] = {
    ["key1"] = 12;
    ["key2"] = "name_4";
    ["Mac"] = {
      [1] = "ID2";
      [2] = "ID3";
    };
  };
}

-- Specifying multiple keys works too:
deep_print(filter(sample, {key2 = "name_3", Str = "sample_string_2"}))
-- outputs
{
  [1] = {
    ["key1"] = 10;
    ["key2"] = "name_3";
    ["Str"] = "sample_string_2";
  };
}

如您所见,filter_row() 是非递归的。它仅对行级键进行操作。如果您的架构是平面的,这应该可以工作,如示例中所示。如果您要过滤的数据实际上更复杂,请提供更多示例。

通过首先将值包装在数组(表)中,使键比较的操作变得更简单。这允许对所有可能的情况采用统一的比较方法(有一些额外的开销)。

这是我对 SO 的第一个答案,如果有任何问题,请编辑/评论。谢谢。

【讨论】:

  • 我期待一个更递归的解决方案来解决我的问题,它可以处理更深层次的表。但是,这个解决方案解决了我的问题。
猜你喜欢
  • 2017-05-10
  • 2023-02-02
  • 2021-07-03
  • 2011-08-07
  • 2018-03-22
  • 2019-07-08
  • 2020-09-03
  • 2017-12-08
  • 2011-01-02
相关资源
最近更新 更多