neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

commit 94894068794dbb99804cda689b6c37e70376c8ca
parent 6b96122453fda22dc44a581af1d536988c1adf41
Author: Gregory Anders <greg@gpanders.com>
Date:   Wed, 19 Apr 2023 07:05:04 -0600

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)

Diffstat:
Mruntime/doc/lua.txt | 23+++++++++++++++++++----
Mruntime/lua/vim/iter.lua | 67+++++++++++++++++++++++--------------------------------------------
Mtest/functional/lua/vim_spec.lua | 11++++++++---
3 files changed, 50 insertions(+), 51 deletions(-)

diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt @@ -3037,6 +3037,19 @@ Iter:find({self}, {f}) *Iter:find()* Iter:fold({self}, {init}, {f}) *Iter:fold()* Fold an iterator or table into a single value. + Examples: > + + -- 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 } +< + Parameters: ~ • {init} any Initial value of the accumulator. • {f} function(acc:any, ...):A Accumulation function. @@ -3297,9 +3310,7 @@ Iter:totable({self}) *Iter:totable()* 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 iterator pipeline, each value will be included in a table. Examples: >lua @@ -3310,9 +3321,13 @@ Iter:totable({self}) *Iter:totable()* -- { { 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 } } < + 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) diff --git a/runtime/lua/vim/iter.lua b/runtime/lua/vim/iter.lua @@ -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 diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua @@ -3374,13 +3374,18 @@ describe('lua stdlib', function() end) it('handles map-like tables', function() - local t = { a = 1, b = 2, c = 3 } - local it = vim.iter(t):map(function(k, v) + local it = vim.iter({ a = 1, b = 2, c = 3 }):map(function(k, v) if v % 2 ~= 0 then return k:upper(), v * 2 end end) - eq({ A = 2, C = 6 }, it:totable()) + + local t = it:fold({}, function(t, k, v) + t[k] = v + return t + end) + eq({ A = 2, C = 6 }, t) + end) it('handles table values mid-pipeline', function() local map = {