Lua doesn't go over 'nil' in table - arrays

I have such an anonymous Lua function:
an_func = function(x) return x == nil end
I need to test it on a table from 4 values: {'None', nil, 1, '1'}.
So, I wrote the following code:
for num, element in pairs({'None', nil, 1, '1'}) do
print(num .. ': ' .. tostring(an_func(element)))
end
When I ran it, I got only three lines:
1: false
3: false
4: false
The line with the true value (corresponding to nil table element) didn't print.
Can you explain, why did Lua print all results, but true? How can I fix the snipped make it output all 4 lines?
P.S. I'm newbee in Lua.

When using pairs(), it goes over all elements of a table. nil means nothing, so it won't go over it. Why should it tell you there's nothing in the table? If you have a totally new table, it'll return nil if you index it with anything. print(({}).test) --> nil
If you really want to get the nil, you could use a dummy value:
local NIL = setmetatable({},{__tostring=function() return "nil" end})
an_func = function(x) return x == NIL end
for num, element in pairs({'None', NIL, 1, '1'}) do
print(num .. ': ' .. tostring(an_func(element)))
end
Output:
1: false
2: true
3: false
4: false

In Lua, everything (a function, string, thread, ...) is a value, with nil (and technically none/void) being the only exception(s). To put simply, nil represents the absence of value. So when iterating over a table, why would Lua say there is nothing there. If it did it would have to iterate over every virtually possible index you can create.
However it appears you are using an array. You technically can iterate over it assuming you know the length. I'll make the assumption that the last item in the table is NOT nil, and that there wasn't any holes assigned after the initial length of the table with holes, however this may not necessarily be true in your case, if it isn't you will have to keep track of the length yourself and use it rather than the # operator:
for i = 1,#t do
local v = t[i]
print (v == nil)
end

Other answers address your second question well but the simple explanatory facts are Any key with value nil is not considered part of the table. Conversely, any key that is not part of a table has an associated value nil. Assigning a value of nil removes any existing key-value pair with the same key.

You would need to loop through all keys you want to print the values for.
Possible ways:
Know the length beforehand. (warspyking noted about this)
Using an end marker.
Calculate the length somehow (for example with maxn).
Use something to represent nil values. (EinsteinK showed an example
of this)
Each has their good and bad sides, here are some:
For the first you would need to know the length beforehand in all cases which may be difficult.
The end marker is convenient, but requires you to shift around the end marker.
Calculating length would not work if the last element is nil.
Example of calculating the length - assuming last element is not nil.
Note that lua 5.1 has table.maxn that you can use instead of this custom implementation of maxn.
an_func = function(x) return x == nil end
function maxn(t)
local n = 0
for k,v in pairs(t) do
if k%1 == 0 then
n = k
end
end
return n
end
local t = {'None', nil, 1, '1'}
for num = 1, maxn(t) do
print(num .. ': ' .. tostring(an_func(t[num])))
end
Example of using an end marker:
an_func = function(x) return x == nil end
local END = {}
local t = {'None', nil, 1, '1', END}
local num = 1
while t[num] ~= END do
print(num .. ': ' .. tostring(an_func(t[num])))
num = num+1
end

Related

Array interpreted as a Fixnum

I'm currently learning ruby and I wrote this piece of code :
def multi_gen
s = []
for i in (3..10)
if i%3 == 0 || i%5 == 0
s<<i
end
end
return s
end
puts multi_gen
def rec_sum(num_arr)
if num_arr == []
return 0
else
num_arr.first + rec_sum(num_arr.shift)
end
end
puts rec_sum(multi_gen)
That should return the sum of all 3 and 5 multiples up to 1000.
But I get an error :
myrbfile.rb:17:in `rec_sum': undefined method `first' for 3:Fixnum (NoMethodError)
from villani.rb:17:in `rec_sum'
from villani.rb:21:in `<main>'
But when I re-write it like this :
def multi_gen
s = []
for i in (3..10)
if i%3 == 0 || i%5 == 0
s<<i
end
end
return s
end
puts multi_gen
def rec_sum(num_arr)
if num_arr == []
return 0
else
num_arr[0] + rec_sum(num_arr[1..num_arr.last])
end
end
puts rec_sum(multi_gen)
I don't get the error.
So why is my first rec_sum functions interpretting my Array as a Fixnum in the first case?
The issue is in the recursive call:
rec_sum(num_arr.shift)
Array#shift returns the shifted element, not the remaining array. You should explicitly pass the array as an argument to recursive call:
rec_sum(num_arr[1..-1])
or
rec_sum(num_arr.tap(&:shift))
The latter would [likely] be looking too cumbersome for the beginner, but it’s a very common rubyish approach: Object#tap yields the receiver to the block, returning the receiver. Inside a block (num_arr.tap(&:shift) is a shorthand for num_arr.tap { |a| a.shift } we mutate the array by shifting the element out, and it’s being returned as a result.
mudasobwa already explained why using shift doesn't give the expected result. Apart from that, your code is somehow unidiomatic.
In multi_gen you are creating an empty array and append elements to it using a for loop. You rarely have to populate an array manually. Instead, you can usually use one of Ruby's Array or Enumerable methods to generate the array. select is a very common one – it returns an array containing the elements for which the given block returns true:
(1..1000).select { |i| i % 3 == 0 || i % 5 == 0 }
#=> [3, 5, 6, 9, 10, 12, ...]
In rec_sum, you check if num_arr == []. Although this works, you are creating an empty throw-away array. To determine whether an array is empty, you should call its empty?:
if num_arr.empty?
# ...
end
To get the remaining elements from the array, you use:
num_arr[1..num_arr.last]
which can be abbreviated by passing a negative index to []:
num_arr[1..-1]
There's also drop which might look a little nicer:
num_arr[0] + rec_sum(num_arr[1..-1])
# vs
num_arr.first + rec_sum(num_arr.drop(1))
Another option to get first and remaining elements from an array is Ruby's array decomposition feature (note the *):
def rec_sum(num_arr)
if num_arr.empty?
0
else
first, *remaining = num_arr
first + rec_sum(remaining)
end
end
You could also consider using a guard clause to return from the method early:
def rec_sum(num_arr)
return 0 if num_arr.empty?
first, *remaining = num_arr
first + rec_sum(remaining)
end
Writing recursive methods is great for learning purposed, but Ruby also has a built-in sum method:
multi_gen.sum #=> 234168
or – since you are using an older Ruby version – inject:
multi_gen.inject(0, :+) #=> 234168

Ruby modify array items and return full array

I have this code here
string.split(/(\w{1,}=)/).each_slice(1).map { |i| items << i }
items.map! do |i|
i = i << str if i.to_s =~ /\w{1,}=/
end
puts items*''
And I want to modify certain items in the array based on regex, then return the full array with the modified items in it. This only returns the modified items. How do I achieve what I'm looking for?
EDIT: Ok, so say I'm trying to split a link using this regex:
page.php?site=blah&id=1
The link is split and added to the array which now contains
page.php?
site=
blah&
id=
1
What I want to do is append some value to the end of the elements ending with a =. This way, when I return the modified array as a string it would output like this:
page.php?site=(newval)&id=(newval)
You have several undefined variables in your example, which is very sloppy.
each_slice(1) is equivalent to each(), so it's not clear why you are using each_slice(1). In any case, both each() and map() step through the items in an Array one by one, but each() returns the original Array unchanged. On the other hand, you use map() when you want to create a new Array that contains changes to the items.
In the regex /\w{1,}/, there is a shortcut for the quantifier {1, }, and it's: +, so most people would write the regex as /\w+/, where + means 1 or more.
I want to modify certain items in the array based on regex, then
return the full array with the modified items in it.
Here is an example:
results = [1, 2, 3].map do |num|
if num == 2
num + 4
else
num - 1
end
end
p results
--output:--
[0, 6, 2]
Your current attempt with map() doesn't return anything if the conditional fails. Note how the example above returns something both when the condition fails AND when the condition succeeds. map() replaces an item with whatever is returned for that item.
Now look at this example:
results = [1, 2, 3].map do |num|
if num == 2
num + 4
end
end
p results
--output:--
[nil, 6, nil]
If you don't return something for an item, then map() will use nil for that item. In the example, if the condition num == 2 is true then num+4 is returned--but if num == 2 is false, nothing is returned.
Edit:
words = %w[
page.php?
site=
blah&
id=
1
] #=> words = ["page.php?", "site=", "blah&", "id=", "1"]
suffix = 'hello'
results = words.map do |word|
if word.end_with?('=')
"#{word}#{suffix}"
else
word
end
end
p results
--output:--
["page.php?", "site=hello", "blah&", "id=hello", "1"]
Instead of parsing a URL with a regex, have you considered using the addressable gem?
require 'addressable/uri'
uri = Addressable::URI.parse('page.php?site=blah&id=1&bar')
uri.query_values = uri.query_values.map do |k, v|
[k, v.is_a?(String) ? v << 'foo' : v]
end
puts uri.to_s # => page.php?site=blahfoo&id=1foo&bar
This won't handle very complex query parameters (it will just pass them through).
You can use respond_to? :sub! and v.sub! /$/, 'foo' instead of checking types if that makes you uneasy. (I wouldn't use :<< or :concat because those are valid methods for Arrays.)

How to remove all 'nil' values from an array?

I have an array of objects (or just numbers), and I have another array which contains all the objects that should not be removed from the first array in any circumstances. It looks something like this:
-- Array of objects (just numbers for now)
Objects = {}
-- Array of objects that should always stay in the 'Objects' array
DontDestroyThese = {}
-- Populate the arrays
Objects[#Objects+1] = 1
Objects[#Objects+1] = 2
Objects[#Objects+1] = 3
Objects[#Objects+1] = 4
Objects[#Objects+1] = 5
DontDestroyThese[#DontDestroyThese+1] = 2
DontDestroyThese[#DontDestroyThese+1] = 5
Now, I have a method called destroy() that should remove all objects from the Objects array except those included in the DontDestroyThese array. The method looks something like this:
function destroy()
for I = 1, #Objects do
if(DontDestroyThese[Objects[I]] ~= nil) then
print("Skipping " .. Objects[I])
else
Objects[I] = nil
end
end
end
However, as the result, the Objects array now contains nil values here and there. I'd like to remove these nils so that the Objects array would consist only of the numbers that were left there after calling destroy(). How do I do that?
The most efficient way is probably to create a new table to hold the result. Trying to move values around in the array is likely to have a higher overhead than simply appending to a new table:
function destroy()
local tbl = {}
for I = 1, #Objects do
if(DontDestroyThese[Objects[I]] ~= nil) then
table.insert(tbl, Objects[I])
end
end
Objects = tbl
end
This method also means you don't have to deal with altering the contents of the table/array you're iterating over.
I think the solution is much simpler. To remove any nils ('holes' in your array), all you need to do is iterate your table using pairs(). This will skip over any nils returning only the non-nil values that you add to a new local table that is returned in the end of the 'cleanup' function. Arrays (tables with indices from 1..n) will remain with the same order. For example:
function CleanNils(t)
local ans = {}
for _,v in pairs(t) do
ans[ #ans+1 ] = v
end
return ans
end
Then you simply need to do this:
Objects = CleanNils(Objects)
To test it:
function show(t)
for _,v in ipairs(t) do
print(v)
end
print(('='):rep(20))
end
t = {'a','b','c','d','e','f'}
t[4] = nil --create a 'hole' at 'd'
show(t) --> a b c
t = CleanNils(t) --remove the 'hole'
show(t) --> a b c e f
local function remove(t, pred)
for i = #t, 1, -1 do
if pred(t[i], i) then
table.remove(t, i)
end
end
return t
end
local function even(v)
return math.mod(v, 2) == 0
end
-- remove only even numbers
local t = remove({1, 2, 3, 4}, even)
-- remove values you want
local function keep(t)
return function(v)
return not t[v]
end
end
remove(Objects, keep(DontDestroyThese))

Lua loop knowing when loops ends

Is there a statement in Lua that would allow me to determinate whether it's last loop cycle?
When I'm not able to determinate how many time loop will loop?
Example:
for _, v in pairs(t) do
if I_know_this_is_your_last_cycle then
-- do something
end
This is simplified version of missingno's answer: :-)
for _, v in pairs(t) do
if next(t,_) == nil then
-- do something in last cycle
end
end
You could probably try to write something like this:
--count how many elements you have in the table
local element_cnt = 0
for k,v in pairs(t) do
element_cnt = element_cnt + 1
end
local current_item = 1
for k,v in pairs(t)
if current_item == element_count then
print "this is the last element"
end
current_item = current_item + 1
end
or this:
local last_key = nil
for k,v in pairs(t) do
last_key = k
end
for k,v in pairs(t) do
if k == last_key then
--this is the last element
end
end
In general, no. As you can see from the Lua docs, the for loop is syntax sugar for a while loop on top of an iterator so it only knows if the loop is over at the start of the loop.
If you really want to check if you are entering the last iteration then I would simply code things explicitly with an while loop.
local curr_val = initial_value
while curr_val ~= nil then
local next_val = get_next(initial_value)
if next_val ~= nil then
--last iteration
else
--not last iteration
end
curr_val = next_val
end
If you want to translate the example with the pairs function, you can use the next function as the iterator.
As an aside, I would suggest thinking twice before coding a loop like this one. The way its coded means its very easy to write code that doesn't work right when you are iterating over 0 or 1 elements or to write code that doesn't treat the last element properly. Most of the time writing a plain loop and putting the "at the end" code after the loop is more reasonable.
There are a couple of ways to do this. The easiest is to just use the standard for loop and check it yourself, like this:
local t = {5, nil, 'asdf', {'a'}, nil, 99}
for i=1, #t do
if i == #t then
-- this is the last item.
end
end
Alternatively, you could roll your own iterator function for tables that tells you when you're on the last item, something like this:
function notifyPairs(t)
local lastItem = false
local i = 0
return
function()
i = i + 1
if i == #t then lastItem = true end;
if (i <= #t) then
return lastItem, t[i]
end
end
end
for IsLastItem, value in notifyPairs(t) do
if IsLastItem then
-- do something with the last item
end
end

How can I check if a lua table contains only sequential numeric indices?

How can I write a function that determines whether it's table argument is a true array?
isArray({1, 2, 4, 8, 16}) -> true
isArray({1, "two", 3, 4, 5}) -> true
isArray({1, [3]="two", [2]=3, 4, 5}) -> true
isArray({1, dictionaryKey = "not an array", 3, 4, 5}) -> false
I can't see any way of finding out if the numeric keys are the only keys.
EDIT: Here's a new way to test for arrays that I discovered just recently. For each element returned by pairs, it simply checks that the nth item on it is not nil. As far as I know, this is the fastest and most elegant way to test for array-ness.
local function isArray(t)
local i = 0
for _ in pairs(t) do
i = i + 1
if t[i] == nil then return false end
end
return true
end
ipairs iterates over indices 1..n, where n+1 is the first integer index with a nil value
pairs iterates over all keys.
if there are more keys than there are sequential indices, then it cannot be an array.
So all you have to do is see if the number of elements in pairs(table) is equal to the number of elements in ipairs(table)
the code can be written as follows:
function isArray(tbl)
local numKeys = 0
for _, _ in pairs(tbl) do
numKeys = numKeys+1
end
local numIndices = 0
for _, _ in ipairs(tbl) do
numIndices = numIndices+1
end
return numKeys == numIndices
end
I'm pretty new to Lua, so there might be some builtin function to reduce the numKeys and numIndices calculations to simple function calls.
By "true array", I suppose you mean a table whose keys are only numbers. To do this, check the type of every key of your table. Try this :
function isArray(array)
for k, _ in pairs(array) do
if type(k) ~= "number" then
return false
end
end
return true --Found nothing but numbers !
end
Note: as #eric points out, pairs is not defined to iterate in a specific order. Hence this is no valid answer.
The following should be sufficient; it checks that the keys are sequential from 1 until the end:
local function isArray(array)
local n = 1
for k, _ in pairs(array) do
if k ~= n then return false end
n = n + 1
end
return true
end
I wrote this code for another similar question lately:
---Checks if a table is used as an array. That is: the keys start with one and are sequential numbers
-- #param t table
-- #return nil,error string if t is not a table
-- #return true/false if t is an array/isn't an array
-- NOTE: it returns true for an empty table
function isArray(t)
if type(t)~="table" then return nil,"Argument is not a table! It is: "..type(t) end
--check if all the table keys are numerical and count their number
local count=0
for k,v in pairs(t) do
if type(k)~="number" then return false else count=count+1 end
end
--all keys are numerical. now let's see if they are sequential and start with 1
for i=1,count do
--Hint: the VALUE might be "nil", in that case "not t[i]" isn't enough, that's why we check the type
if not t[i] and type(t[i])~="nil" then return false end
end
return true
end
Here's my take on this, using #array to detect a gap or stop when too many keys have been read:
function isArray(array)
local count=0
for k,_ in pairs(array) do
count=count+1
if (type(k) ~= "number" or k < 1 or k > #array or count > #array or math.floor(k) ~= k) then
return false
end
end
if count ~= #array then
return false
end
return true
end
Iterate from 0 to the number of elements, and check if all elements with the counter's index exist. If it's not an array, some indexes will miss in the sequence.

Resources