I have a LUA table:
local tableDatabase = {
name = uniqueName,
class = val_one_to_eight, --not unique
value = mostly_but_not_guaranteed_unique_int}
This table can be sorted by any of the above and may contain a very large data set.
Right now in order to insert I'm just iterating through the table ipairs until I find:
insertedvalue.uniqueName > tableDatabase.uniqueName
--(or comparing the other parms instead if they are the selected sort order.)
I need this function to work super fast. Is there a search algorithm someone could recommend for finding the index into the table to insert or some method I could use that would work on a lua table to optimize this for speed of insertions?
As I know, for strictly ordered structure you can use binary search or similar algorithms.
Lua Users provides ready to use function.
Why don't you create an index on name? If it is not fast enough, you can make __index less generic, i.e. hardcoding the only index on name.
-- Returns a table. ... is a list of fields, for which unique indices should be created:
function indexedTable (...)
local t = {
__indices = {},
__insert = function (self, value) -- instead of table.insert.
self [#self + 1] = value -- implicily calls metamethod __newindex.
end
}
-- Initialise indices:
for _, index in ipairs {...} do
t.__indices [index] = {}
end
setmetatable (t, {
-- Allow t [{name = 'unique'}]:
__index = function (t, key)
if type (key) == 'table' then
for index_key, index_value in pairs (key) do
local value = t.__indices [index_key] [index_value]
if value then
return value
end
end
else
return rawget (t, key)
end
end,
-- Updates all indices on t [k] = v, but doesn't work on table.insert, so use t:__insert"
__newindex = function (t, key, value)
-- insert uniqueness constraint here, if you want.
for index_key, index in pairs (t.__indices) do
index [value [index_key]] = value
end
rawset (t, key, value)
end
})
return t
end
-- Test:
local tableDatabase = indexedTable ('name')
-- Not table.insert, as it is not customizable via metamethods:
tableDatabase:__insert {
name = 'unique1',
class = 1,
value = 'somewhat unique'
}
tableDatabase:__insert {
name = 'unique2',
class = 2,
value = 'somewhat unique'
}
tableDatabase:__insert {
name = 'unique3',
class = 2,
value = 'somewhat unique but not absolutely'
}
local unique2 = tableDatabase [{name = 'unique2'}] -- index search.
print (unique2.name, unique2.class, unique2.value)
Related
I Have two maps:
def map = ['a': 3, 'b': 4, 'c':5]
def map2= ['a': 3, 'b': 4, 'c':4]
I want to take the maximum value of the map like this:
def newMap = map.max {it.value}
and my output is correct ('c':5), my problem is with the second map because there is more than one max value. On this case I want to change the key so I can know that there was more than one max value. I want my output to be:
def newMap2 = map2.max {it.value}
assert newMap2 == ['+than1': 4]
Can I alter the key of the map in this specific case using groovy functions?
Can I achieve this inside the max closure?
I do not want to alter the key if there isn't more than one max value.
Keep in mind that map.max(Closure cl) returns Map.Entry<String, Integer> and not a map. So if you expect a map with a single key, you will have to create one from the result you get.
Map.max(Closure cl) searches for the maximum value and returns it. There is no variant that allows you to modify function behavior in case of two entries holding maximum value. According to docs:
Selects an entry in the map having the maximum
calculated value as determined by the supplied closure.
If more than one entry has the maximum value,
an arbitrary choice is made between the entries having the maximum value.
In practice: the first entry found with maximum value is returned.
Groovy however offers a different collection function that can be used to achieve what you expect - Collection.inject(initialValue, closure) function. This is an equivalent of popular fold function known very well in functional programming paradigm. It starts with some initial value and it iterates a collection and applies a function to every element (this function returns a new value that replaces value passed as initial value) to reduce a list of elements to a single element in a single iteration. Collection.inject() javadoc gives a very descriptive example of summing all numbers from a list:
assert 0+1+2+3+4 == [1,2,3,4].inject(0) { acc, val -> acc + val }
Now let's take a look how we can use this function to achieve expected result. Consider following example:
def map2= ['a': 3, 'b': 4, 'c':4]
Tuple2<String, Integer> result = map2.inject(new Tuple2<String, Integer>(null, null)) { Tuple2<String, Integer> tuple, entry ->
entry.value >= tuple.second ?
new Tuple2<>(entry.value == tuple.second ? '+1than1' : entry.key, entry.value) :
tuple
}
assert result.first == '+1than1'
assert result.second == 4
Here we start with new Tuple2<String, Integer>(null, null) as an initial value - a Tuple2 represents a pair of two values. In our case it represents a key and the maximum value. The closure checks if current entry value is higher or equal the one we store in the tuple and if this is true it checks if the value is the same as the one we have already found - if this is true it uses +than1 as a key instead of a key taken from map. Otherwise it simply uses entry key without any modification. When the value is lower then the one we currently store in the tuple, existing tuple gets returned. And finally, we get a tuple where tuple.first holds a key and tuple.second holds a maximum value.
Of course to make the inject part more readable it is worth extracting a data class that represents your expected result and behavior. You can implement a function like compareAndReplace there to define specific behavior when another maximum value is found. Something like this:
import groovy.transform.Immutable
#Immutable
class Result {
static Result EMPTY = new Result(null, null)
String key
Integer value
Result compareAndReplace(Map.Entry<String, Integer> entry, String key = '+1than1') {
entry.value >= value ?
new Result(entry.value == value ? key : entry.key, entry.value) :
this
}
}
def map2 = ['a': 3, 'b': 4, 'c': 4]
Result result = map2.inject(Result.EMPTY) { Result result, entry -> result.compareAndReplace(entry) }
assert result.key == '+1than1'
assert result.value == 4
You can do it in a one-liner but it's still a little obscure:
println map2.groupBy{ it.value }.max{ it.key }.value.with{ it.size() > 1 ? ['+than1': it.values()[0]] : it }
I'd probably at least extract out the last Closure:
def mergedKey = { it.size() > 1 ? ['+than1': it.values()[0]] : it }
def newMap2 = map2.groupBy{ it.value }.max{ it.key }.value.with(mergedKey)
assert newMap2 == ['+than1': 4]
Let's say I have a map like this:
def map = [name: 'mrhaki', country: 'The Netherlands', blog: true, languages: ['Groovy', 'Java']]
Now I can return "submap" with only "name" and "blog" like this:
def keys = ['name', 'blog']
map.subMap(keys)
// Will return a map with entries name=mrhaki and blog=true
But is there a way to easily return multiple values instead of a list of entries?
Update:
I'd like to do something like this (which doesn't work):
def values = map.{'name','blog'}
which would yield for example values = ['mrhaki', true] (a list or tuple or some other datastructure).
map.subMap(keys)*.value
The Spread Operator (*.) is used to invoke an action on all items of
an aggregate object. It is equivalent to calling the action on each
item and collecting the result into a list
You can iterate over the submap and collect the values:
def values = map.subMap(keys).collect {it.value}
// Result: [mrhaki, true]
Or, iterate over the list of keys, returning the map value for that key:
def values = keys.collect {map[it]}
I would guess the latter is more efficient, not having to create the submap.
A more long-winded way to iterate over the map
def values = map.inject([]) {values, key, value ->
if (keys.contains(key)) {values << value}
values
}
For completeness I'll add another way of accomplishing this using Map.findResults:
map.findResults { k, v -> k in keys ? v : null }
flexible, but more long-winded than some of the previous answers.
I have list of Objects(Name A), A have property B and C. I need to find the object in the list which B property equal with another object's C property. For Example:
def objectList = [A1,A2,A3,A4,A5,A6,A7,A8];
if A1.B == A2.C then return A1,A2;
Any good way to do that?
You can use the findAll method for this:
def list = []
def matching = list.findAll { A a ->
a.B == a.C
}
Update
You can get all the pairs of matching objects this way:
def matching = []
list.unique { A a1, A a2 ->
if (a1.B == a2.C || a1.C == a2.B) {
matching << a1 << a2
}
return 1
}
This is kind of a hacky solution since it does not use the unique method as intended.
Not sure whether you want your result flattened or not, anyway here's a solution returning a list of tuples:
def result = list.inject([]) {acc,a1->
list.each {a2->
if (!a1.is(a2) && a1.b == a2.c) {
acc << [a1,a2]
}
}
acc
}
I am trying to remove mainMenuButtonGroup1 through mainMenuButtonGroup6.
To do this I think I need to do the following: [mainMenuButtonGroup..n] How would I do that?
local mainMenuButtonGroup1 = { 1,2 }
local mainMenuButtonGroup2 = { 3,4 }
local mainMenuButtonGroup3 = { 5,6 }
local mainMenuButtonGroup4 = { 7,8 }
local mainMenuButtonGroup5 = { 9,10 }
local mainMenuButtonGroup6 = { 11,12 }
for n = 1, 6 do
[mainMenuButtonGroup..n] = nil
end
Based on your comments, I assume this is something like what you want to do, where t is the table that you mentioned.
for i = 1, 6 do
local k = 'mainMenuButtonGroup' .. i
t[k]:removeSelf()
t[k] = nil
end
String concatenation is performed by the .. double period operator, and table indexing via . or []. In this case, you're indexing via [] because you're using a variable and not a constant name.
Edit: Put your similar local variables into a table instead. It makes it far, far easier to iterate as a group. In fact, the only way I know of to iterate locals is through the debug library.
local mainMenuButtonGroup = { {1,2}, {3,4}, {5,6}, {7,8}, {9,10} }
table.insert(mainMenuButtonGroup, {11,12})
for i = 1, 6 do
mainMenuButtonGroup[i] = nil
end
Note, however, that setting a numeric index in a table to nil does not rearrange the table's numerically indexed values. I.e., you'll leave the table with holes. See table.remove.
Assuming the variables are global, they will be in the _G table:
for n = 1, 6 do
for i = _G['mainMenuButtonGroup'..n].numChildren, 1, -1 do
_G['mainMenuButtonGroup'..n][i]:removeSelf()
_G['mainMenuButtonGroup'..n][i] = nil
end
end
If they are local, you can use debug.getlocal, but you will have to loop over all the locals until you find the names you want. Not ideal, but possible.
How would you print() out or find out the index of an object?
For example, if I spawned 20 random rock objects on screen into an array RockTable = {};
Like this RockTable[#RockTable + 1] = rock;
And all 20 rocks are displayed on screen how would I find out what key or index each rock has by clicking on them?
I'm using Corona SDK.
Any help would be greatly appreciated.
Invert the table:
function table_invert(t)
local u = { }
for k, v in pairs(t) do u[v] = k end
return u
end
You can then use the inverted table to find the index.
I find this function so useful that it goes into my permanent "Lua utilities" libraries.
There's another way you can do it, using metamethods.
[Edited to allow you to remove values too]
t = {} -- Create your table, can be called anything
t.r_index = {} -- Holds the number value, i.e. t[1] = 'Foo'
t.r_table = {} -- Holds the string value, i.e. t['Foo'] = 1
mt = {} -- Create the metatable
mt.__newindex = function (self, key, value) -- For creating the new indexes
if value == nil then -- If you're trying to delete an entry then
if tonumber(key) then -- Check if you are giving a numerical index
local i_value = self.r_index[key] -- get the corrosponding string index
self.r_index[key] = nil -- Delete
self.r_table[i_value] = nil
else -- Otherwise do the same as above, but for a given string index
local t_value = self.r_table[key]
self.r_index[t_value] = nil
self.r_table[key] = nil
end
else
table.insert(self.r_index, tonumber(key), value) -- For t[1] = 'Foo'
self.r_table[value] = key -- For t['Foo'] = 1
end
end
mt.__index = function (self, key) -- Gives you the values back when you index them
if tonumber(key) then
return (self.r_index[key]) -- For For t[1] = 'Foo'
else
return (self.r_table[key]) -- For t['Foo'] = 1
end
end
setmetatable(t, mt) -- Creates the metatable
t[1] = "Rock1" -- Set the values
t[2] = "Rock2"
print(t[1], t[2]) -- And *should* proove that it works
print(t['Rock1'], t['Rock2'])
t[1] = nil
print(t[1], t[2]) -- And *should* proove that it works
print(t['Rock1'], t['Rock2'])
It's more versatile as you can copy the t value and take it with you; it also means that you only have to play around with the one variable most of the time - hopefully should reduce the likelihood of you trying to access the wrong thing.
The simplest way is to add an "index" property to each rock:
RockTable = {}
for i=1,20 do
local rock
-- do your thing that generates a new 'rock' object
rock.index = #RockTable + 1
RockTable[rock.index] = rock
end
If you use a touch listener method, you can retrieve the rock this way:
function touchListener( event )
local rock = event.target
local rockIndex = rock.index
-- ...
end
It is true that you can maintain a second table with indices, but I find my method cleaner - when it is time to remove things, you only have to worry about one table, the main one.
I have a question though: why do you need to retrieve that index? In most cases, well designed event listener functions are enough, you don't need to "find" your objects. Of course I lack information on what you are trying to do, but it is possible that you are over-complicating things.
you could do something like this to save you some trouble of constantly looping over a table to find the index...
RockTable = {}
RockIndicies = {}
for i = 1, 20 do
idx = #RockTable + 1
RockTable[idx] = rock
RockIndicies[rock] = idx
end
then when you need to know the index, you can just use the rock you have to index RockIndices to quickly get it. If you 'delete' a rock, you'd want to make sure to remove it in both places.
Unfortunately you'd need to brute the table, to my knowledge. Although, to know that one was clicked, wouldn't you need to be looping them in some way anyway; and therefore already know the index?
Edit
Oh, unless Corona has some sort of callback event for clicking. I've never used it, I've got experience in Lua though.
You could maybe do a backwards reference, like so:
Rocks = {a rock, a rockB, a rockC}
RocksB = {[a rock] = 1, [a rockB] = 2, [a rockC] = 3}
Then just say rockNum = RocksB[rock]
I'm pretty certain that should work but I can't guarantee it, worth a try though.
Edit2
The brute method would look somewhat like:
function getRock(rock)
for _,v in pairs(rocks) do
if (v == rock)
return _
end
end
return "Rock does not exist."
end