Sort multiple lists lua - sorting

How can I sort 2 lists y location ( map tiles and people ) and draw them in order dependent of y. 2 lists I want to use:
map = {}
map.y = {60,10,40,80}
map.t = {0,0,1,1} -- type
people = {}
people.y = {0,100}
people.t = {0,1} -- type
I can currently sort and draw a single list of hero and boxes.
Sort / draw code:
box1 = love.graphics.newImage("box1.png")
box2 = love.graphics.newImage("box2.png")
box3 = love.graphics.newImage("box3.png")
hero = love.graphics.newImage("hero.png")
object = {
x = {0, 50,100,200},
y = {0,200, 50,100},
g = {0,1,2,3}
}
function sortIndex(item)
local i
local id = {} -- id list
for i = 1, #item.x do -- Fill id list (1 to length)
id[i] = i
end
-- print( unpack(id) ) -- Check before
table.sort(id,sortY)-- Sort list
-- print( unpack(id) ) -- Check after
item.sort = id -- List added to object.sort
-- Sort id, using item values
function sortY(a,b)
return item.y[a] < item.y[b]
end
end
function drawObject()
local i,v, g,x,y
for i = 1, #object.x do
v = object.sort[i] -- Draw in order
x = object.x[v]
y = object.y[v]
g = object.g[v]
if g == 0 then g = hero -- set to an image value
elseif g == 1 then g = box1
elseif g == 2 then g = box2
elseif g == 3 then g = box3
end
love.graphics.draw(g,x,y,0,7,7)
end
end
Update sort:
sortIndex(object)
My function sorts an id list comparing a y location list. The id is used to draw objects in order dependent of their y. How can I sort 2 id lists together comparing 2 y location lists, then draw them in order?
Maybe when drawing, switch from map tiles to people dependent on y, but I don't know how.

Might be related to your previous question a lot: Returning A Sorted List's Index in Lua
I assume if your height can be 1,2 and 3 (with 1 being on the top), you first want to render all tiles at Y1, then all people at Y1, then Y2 and Y3. To do that, you'll have to make a combined list and sort that:
map = {}
map.y = {60,10,40,80}
map.t = {0,0,1,1} -- type
people = {}
people.y = {0,100}
people.t = {0,1} -- type
local all = {}
local map_y = map.y
local offset = #map_y
local people_y = people.y
-- Fill the list with map tiles
for i=1,offset do
all[i] = {1,i,map_y[i]} --{type,index,y}
end
-- Fill the list with people
for i=1,#people_y do
all[i+offset] = {2,i,people_y[i]}
end
-- Do the sorting
-- It works a bit like your previous question:
-- 'all' contains "references":
-- They tell us is it's from map/people + the index
-- We sort the references using the third element in it:
-- The 'y' variable we put there during the first 2 loops
table.sort(all,function(a,b)
return a[3] < b[3]
end)
-- Printing example
-- The references are sorted using the 'y' field of your objects
-- With v[1] we know if it's from map/people
-- The v[2] tells us the index in that ^ table
-- The v[3] is the 'y'-field. No real need to remove it
for k,v in pairs(all) do
print(v[1] == 1 and "Map" or "Person",v[2],"with y being",v[3])
end
Output:
Person 1 with y being 0
Map 2 with y being 10
Map 3 with y being 40
Map 1 with y being 60
Map 4 with y being 80
Person 2 with y being 100
There are 2 things I want to add, that doesn't have anything to do with the question of my answer:
Maybe it would be easier if you have a table for each element.
Your people would be {0,0} and {100,1} which might be easier to manipulate.
If you prefer your stuff always sorted, you might want to use this: Sorted List. If you keep a sorted list of all your objects, you don't have to sort the list everytime you add/remove an element, or worse, each time you render. (depending if people move) This might help with performance if you're planning to have a lot of map/people objects. (Sorted List could be useful for your current data system, but also the {y=1,t=1} one)

function sortIndex(...)
sorted = {} -- global
local arrays_order = {}
for arr_index, array in ipairs{...} do
arrays_order[array] = arr_index
for index = 1, #array.y do
table.insert(sorted, {array = array, index = index})
end
end
table.sort(sorted,
function (a,b)
local arr1, arr2 = a.array, b.array
local ind1, ind2 = a.index, b.index
return arr1.y[ind1] < arr2.y[ind2] or
arr1.y[ind1] == arr2.y[ind2] and arrays_order[arr1] < arrays_order[arr2]
end)
end
function drawAll()
for _, elem_info in ipairs(sorted) do
local array = elem_info.array
local index = elem_info.index
local x = array.x[index]
local y = array.y[index]
if array == map then
-- draw a map tile with love.graphics.draw()
elseif array == people then
-- draw a human with love.graphics.draw()
end
end
end
sortIndex(map, people) -- to draw map tiles before people for the same y

Related

get pairs / triple / quadruple... of elements from vector by function

I have a vector with a couple of elements and I want to write a function that returns me all combinations of x items from this vector.
The following code produces the right output for the case x=2 or x=3 or x=4.
However, I can not implement a solution for every possible x following this idea.
values = {'A','B','C','D','E'};
n = length(values);
data2 = {}; % case x=2
for i = 1:n
for j = i+1:n
data2{end+1} = {values{i}, values{j}};
fprintf('%s %s\n',values{i}, values{j})
end
end
data3 = {}; % case x=3
for i = 1:n
for j = i+1:n
for k = j+1:n
data3{end+1} = {values{i}, values{j}, values{k}};
fprintf('%s %s %s\n',values{i}, values{j}, values{k})
end
end
end
data4 = {}; % case x=4
for i = 1:n
for j = i+1:n
for k = j+1:n
for l = k+1:n
data4{end+1} = {values{i}, values{j}, values{k}, values{l}};
fprintf('%s %s %s %s\n',values{i}, values{j}, values{k}, values{l})
end
end
end
end
How would a function look like which would be able to return my data variable?
data = getCombinations(values, x) %values is vector with elements, x is integer value
EDIT
The following code comes pretty close:
data = perms(values)
data = data(:,1:x)
data = unique(data,'rows')
but it still produces output like A,B and B,A
EDIT2
This fixed it somehow but it is not very nice to look at and it does not work for text entries in cells but only for numbers
data = perms(values)
data = data(:,1:x)
data = sort(data,2)
data = unique(data,'rows')
EDIT3
This did it but it is not very nice to look at... Maybe there is a better solution?
function [data] = getCombinations(values,x)
i = 1:length(values);
d = perms(i);
d = d(:,1:x);
d = sort(d,2);
d = unique(d,'rows');
data = v(d);
end
If you don't want repetitions (and your example suggests you don't) then try nchoosek as nchoosek(1:n, x) to give indices:
values = {'A','B','C','D','E'};
n = length(values);
x = 3;
C = nchoosek(1:n, x);
data = values(C)
In the above, each row is a unique combination of 3 of the 5 elements of values.
Alternatively pass in the values directly:
data = nchoosek(values, x);

Classifying true positives, false positives and false negatives using 2 sets of coordinates

Let's say I have two sets of coordinates. Set A is the ground truth coordinates and set B is the newly generated coordinates.
I am classifying the following way:
True positives if the coordinate in set A is at least within 5 pixels of a coordinate in B.
False negative for all the coordinates in set A that do not find matches with any coordinates in set B.
False positive for all the coordinates in set B that do find matches with any coordinates in set A.
The sets do not correspond. By that I mean, the first coordinate in set A doesn't have any relevance with the first coordinate in set B.
Here is my code:
clear;
w = warning ('off','all');
coordA = dir('./GT*.txt');
coordB = dir('./O*.txt');
for i =1:length(coordA)
TP = [];
FP = [];
FN = [];
%read coordinate files
fir = fopen(coordA(i).name, 'r');
disp(coordA(i).name);
A = textscan(fir, '%d %d\n');
fclose(fir);
disp(coordB(i).name);
sec = fopen(coordB(i).name, 'r');
B = textscan(fir, '%d, %d\n');
fclose(sec);
A_x = A{1};
A_y = A{2};
B_x = B{1};
B_y = B{2};
for j = 1:length(A_x)
flag = 1; %this flag indicates false negatives
for k = 1:length(B_x)
X = [A_x(j), A_y(j); B_x(k), B_y(k)];
d = pdist(X);
if(d <= 5)
flag = 0; %Ax and Ay
%the problem is here---------
TP = [TP [B_x(k) B_y(k)]];
B_x(k) = 0;
B_y(k) = 0;
end
end
if(flag)
FN = [FN [A_x(j) A_y(j)]];
end
end
for b = find(B_x)
FP = [FP [B_x(b) B_y(b)]];
end
end
The problem(please note the comments in the code and example below) I am facing is the following. Let's say there are two coordinates in set A that are really close to each other. When I go to check for TPs in set B and I find a coordinate that's within 5 pixels, I mark it as true positive then remove that coordinate from set B. However, let's say I'm trying to check for the other nearby coordinate from set A. Well, it will be marked as a false negative since I removed a close by coordinate in set B when checking for a different coordinate.
I thought of not removing coordinates in set B even when I find a true positive, but then how would I find false positives?
I did this in Matlab, but any language is fine with me.
Example coordinates:
A:
250 500
251 500
B:
250 501
The second coordinate should also be considered as true positive, but its being considered as false negative.
By altering your code, I believe the following part should do what you are looking for. Basically, instead of removing entries, you can just use logical indexing:
clear;
w = warning ('off','all');
coordA = dir('./GT*.txt');
coordB = dir('./O*.txt');
radius = 5;
for i =1:length(coordA)
% read coordinate files
fir = fopen(coordA(i).name, 'r');
disp(coordA(i).name);
A = textscan(fir, '%d %d\n');
fclose(fir);
disp(coordB(i).name);
sec = fopen(coordB(i).name, 'r');
B = textscan(fir, '%d, %d\n');
fclose(sec);
A_x = A{1};
A_y = A{2};
B_x = B{1};
B_y = B{2};
% Initialize logical arrays to reflect the
% Status of each entry
TP = zeros(length(A_x),1); % no entry of A is considered true positive
FN = ones(length(A_x),1); % all entries of A are considered false negative
FP = ones(length(B_x),1); % all entries of B are considered false positive
for ij = 1:length(A_x)
for ijk = 1:length(B_x)
X = [A_x(ij), A_y(ij); B_x(ijk), B_y(ijk)];
d = pdist(X)
if (d <= radius)
TP(ij) = 1; % just found a true positive
FP(ijk) = 0;% no more a false positive
end
end
end
% Obtain the lists containing the appropriate values
% For true positive, just use logical indexing and TP array
True_Pos = [A_x(logical(TP))', A_y(logical(TP))'];
% For false negative, remove TP from FN
False_Neg = [A_x(logical(FN-TP))', A_y(logical(FN-TP))'];
% For false positive, whatever remained switched on in FP
False_Pos = [B_x(logical(FP))', B_y(logical(FP))'];
end
There are other approaches if you do need to remove entries (see my comment above). Moreover, there is a more clean way to code it but I tried to be explicit and follow your implementation.

Can i declare a local variable in a for loop?

for x = 1, 16 do
for y = 1, 16 do
local cntr = Center:new()
cntr.point = {x = 0.5 + x - 1, y = 0.5 + y - 1}
centerLookup[cntr.point] = cntr
table.insert(self.centers, cntr)
end
end
In the code above, centerLookup[point] is meant to look up the respective Center object by inputting a point location.
However, when I try to do this:
function neighbors(center, sqrtsize)
if center.point.y + 1 < sqrtsize then
local up = {x = center.point.x, y = center.point.y+1}
local centerup = centerLookup[up]
table.insert(center.neighbors, centerup)
end
end
centerup returns as a nil value
Idk if the problem is that I can't use a table as an index, but that is what I'm thinking.
Anybody know what's wrong here?
P.S. if it's helpful, centers start at 0.5 (so [0.5, 0.5] would be the first center, then [0.5, 1.5], etc.)
Thanks in advance!
This has nothing to do with local variables and everything to do with the fact that tables are compared by-reference and not by-value.
In Lua, tables are reference types that have their own identity. Even if two tables have the same contents, Lua does not consider them equal unless they are the exact same object.
To illustrate this, here is some sample code, and the printed values:
local tbl1 = {x = 0.5, y = 0.5}
local tbl2 = tbl1
local tbl3 = {x = 0.5, y = 0.5}
print(tbl1 == tbl2) -- True; tbl1 and tbl2 both reference the same table
print(tbl1 == tbl3) -- False; tbl1 and tbl3 reference different tables
local up = {x = center.point.x, y = center.point.y+1}
local centerup = centerLookup[up]
In this snippet, up is a completely new table with only one reference (the up variable itself). This new table won't be a key in your centerLookup table, even if a table key exists with the same contents.
cntr.point = {x = 0.5 + x - 1, y = 0.5 + y - 1}
centerLookup[cntr.point] = cntr
table.insert(self.centers, cntr)
In this snippet, you create a new table, and reference it in three different places: cntr.point, centerLookup as a key, and self.centers as a value. You presumably iterate through the self.centers array, and use the exact same table to look up items in the centerLookup table. However, if you were to use a table not in the self.centers array, it would not work.
Colonel Thirty Two explained the reason why your code not working as expected. I just want to add quick solution:
function pointToKey(point)
return point.x .. "_" .. point.y
end
Use this function for lookup in both places
--setup centerLookup
centerLookup[pointToKey(cntr.point)] = cntr
--find point from lookup
local centerup = centerLookup[pointToKey(up)]

Math.random specific values corona sdk

I have a set of pre-declared values to set specific rotations for an object.
local rotations = {900,-900}
And want my spawn function for the blocks to randomly pick one or the other from this function:
local blocks = {}
timerSrc = timer.performWithDelay(1200, createBlock, -1)
function createBlock(event)
b = display.newImageRect("images/block8.png", 20, 150)
b.x = 500
b.y = math.random(100,250)
b.name = 'block'
physics.addBody(b, "static")
transition.to( b, { rotation = math.random(rotations), time = math.random(2700,3700)} )
blocks:insert(b)
end
When I use:
rotation = math.random(-900,900)
it just chooses any values between the 2 numbers rather than 1 or the other. How can I do this correctly ?
If m is an integer value, math.random(m) returns integers in range [1, m] randomly. So math.random(2) returns integers 1 or 2 randomly.
To generate random numbers either 900 or -900, use:
rotation = math.random(2) == 1 and 900 or -900

copying unordered keys from one table to ordered values in another

I have a table mapping strings to numbers like this:
t['a']=10
t['b']=2
t['c']=4
t['d']=11
From this I want to create an array-like table whose values are the keys from the first table, ordered by their (descending) values in the first table, like this:
T[1] = 'd' -- 11
T[2] = 'a' -- 10
T[3] = 'c' -- 4
T[4] = 'b' -- 2
How can this be done in Lua?
-- Your table
local t = { }
t["a"] = 10
t["b"] = 2
t["c"] = 4
t["d"] = 11
local T = { } -- Result goes here
-- Store both key and value as pairs
for k, v in pairs(t) do
T[#T + 1] = { k = k, v = v }
end
-- Sort by value
table.sort(T, function(lhs, rhs) return lhs.v > rhs.v end)
-- Leave only keys, drop values
for i = 1, #T do
T[i] = T[i].k
end
-- Print the result
for i = 1, #T do
print("T["..i.."] = " .. ("%q"):format(T[i]))
end
It prints
T[1] = "d"
T[2] = "a"
T[3] = "c"
T[4] = "b"
Alexander Gladysh's answer can be simplified slightly:
-- Your table
local t = { }
t["a"] = 10
t["b"] = 2
t["c"] = 4
t["d"] = 11
local T = { } -- Result goes here
-- Store keys (in arbitrary order) in the output table
for k, _ in pairs(t) do
T[#T + 1] = k
end
-- Sort by value
table.sort(T, function(lhs, rhs) return t[lhs] > t[rhs] end)
-- Print the result
for i = 1, #T do
print("T["..i.."] = " .. ("%q"):format(T[i]))
end
Tables in Lua do not have an order associated with them.
When using a table as an array with sequential integer keys from 1 to N, the table can be iterated in order using a loop or ipairs().
When using keys that are not sequential integers from 1 to N, the order can not be controlled. To get around this limitation a second table can be used as an array to store the order of the keys in the first table.

Resources