fix(iter): remove special case totable for map-like tables

This was originally meant as a convenience but prevents possible
functionality. For example:

  -- Get the keys of the table with even values
  local t = { a = 1, b = 2, c = 3, d = 4 }
  vim.iter(t):map(function(k, v)
    if v % 2 == 0 then return k end
  end):totable()

The example above would not work, because the map() function returns
only a single value, and cannot be converted back into a table (there
are many such examples like this).

Instead, to convert an iterator into a map-like table, users can use
fold():

  vim.iter(t):fold({}, function(t, k, v)
    t[k] = v
    return t
  end)
This commit is contained in:
Gregory Anders
2023-04-19 07:05:04 -06:00
parent 6b96122453
commit 9489406879
3 changed files with 50 additions and 51 deletions

View File

@@ -18,14 +18,6 @@ ListIter.__call = function(self)
return self:next()
end
--- Special case implementations for iterators on non-list tables.
---@class TableIter : Iter
local TableIter = {}
TableIter.__index = setmetatable(TableIter, Iter)
TableIter.__call = function(self)
return self:next()
end
---@private
local function unpack(t)
if type(t) == 'table' and t.__n ~= nil then
@@ -185,10 +177,10 @@ end
--- Collect the iterator into a table.
---
--- The resulting table depends on the initial source in the iterator pipeline. List-like tables
--- and function iterators will be collected into a list-like table. If multiple values are returned
--- from the final stage in the iterator pipeline, each value will be included in a table. If a
--- map-like table was used as the initial source, then a map-like table is returned.
--- The resulting table depends on the initial source in the iterator pipeline.
--- List-like tables and function iterators will be collected into a list-like
--- table. If multiple values are returned from the final stage in the iterator
--- pipeline, each value will be included in a table.
---
--- Examples:
--- <pre>lua
@@ -199,9 +191,13 @@ end
--- -- { { 1, 2 }, { 2, 4 }, { 3, 6 } }
---
--- vim.iter({ a = 1, b = 2, c = 3 }):filter(function(k, v) return v % 2 ~= 0 end):totable()
--- -- { a = 1, c = 3 }
--- -- { { 'a', 1 }, { 'c', 3 } }
--- </pre>
---
--- The generated table is a list-like table with consecutive, numeric indices.
--- To create a map-like table with arbitrary keys, use |Iter:fold()|.
---
---
---@return table
function Iter.totable(self)
local t = {}
@@ -239,17 +235,21 @@ function ListIter.totable(self)
return Iter.totable(self)
end
---@private
function TableIter.totable(self)
local t = {}
for k, v in self do
t[k] = v
end
return t
end
--- Fold an iterator or table into a single value.
---
--- Examples:
--- <pre>
--- -- Create a new table with only even values
--- local t = { a = 1, b = 2, c = 3, d = 4 }
--- local it = vim.iter(t)
--- it:filter(function(k, v) return v % 2 == 0 end)
--- it:fold({}, function(t, k, v)
--- t[k] = v
--- return t
--- end)
--- -- { b = 2, d = 4 }
--- </pre>
---
---@generic A
---
---@param init A Initial value of the accumulator.
@@ -783,7 +783,7 @@ function Iter.new(src, ...)
count = count + 1
local v = src[count]
if v == nil then
return TableIter.new(src)
return Iter.new(pairs(src))
end
t[count] = v
end
@@ -827,25 +827,4 @@ function ListIter.new(t)
return it
end
--- Create a new TableIter
---
---@param t table Table to iterate over. For list-like tables, use ListIter.new instead.
---@return Iter
---@private
function TableIter.new(t)
local it = {}
local index = nil
function it.next()
local k, v = next(t, index)
if k ~= nil then
index = k
return k, v
end
end
setmetatable(it, TableIter)
return it
end
return Iter