Looping through tables in a table in Lua - c++

I've hit a complete dead end with this. This is probably going to be something incredibly basic and it will most likely result in me smashing my head into a wall for having a major brain fart. My question is basically, how do you loop though tables in lua if the entries are tables themselves?
C++:
lua_newtable(luaState);
for(auto rec : recpay) {
lua_newtable(luaState);
lua_pushnumber(luaState, rec.amount);
lua_setfield(luaState, -2, "Amount");
lua_pushnumber(luaState, rec.units);
lua_setfield(luaState, -2, "Units");
lua_setfield(luaState, -2, rec.type);
}
lua_setglobal(luaState, "RecuringPayments");
Lua:
for _,RecWT in ipairs(RecuringPayments) do
-- RecWT.Amount = nil?
end

In your C++ code it looks like you're setting the subtable by string as a key rather than by index. To traverse that entry you have to use pairs instead:
for recType, RecWT in pairs(RecuringPayments) do
assert(RecWT.Amount ~= nil)
end
Note that ipairs only traverses the index part of the table, the associative part is ignored.
Alternatively, if you want to use index access then you have to set the key-value with lua_settable instead:
lua_newtable(luaState);
int i = 0;
for(auto rec : recpay)
{
lua_newtable(luaState);
lua_pushnumber(luaState, rec.amount);
lua_setfield(luaState, -2, "Amount");
lua_pushnumber(luaState, rec.units);
lua_setfield(luaState, -2, "Units");
lua_pushnumber(luaState, ++i);
lua_insert(luaState, -2);
lua_settable(luaState, -3);
}
lua_setglobal(luaState, "RecuringPayments");

You can use a recursive function that traverses tables:
function traversetable(tab, array)
local iteratefunc = array and ipairs or pairs
for k, v in iteratefunc(tab) do
if type(v) == "table" then
traversetable(v, array) --Assumes that type is the same as the parent's table
else
--Do other stuff
end
end
end
This is just a basic example, but gives you a rough idea. array is a boolean value indicating, whether it's a one-based array or not.

Related

How can I extract values from within a list

Let us say that I have a Map in dart which holds value in this format : String, List<MyModel>. Now the Map would look something like this -
{
'a' : [MyModel1, MyModel2],
'b' : [MyModel3],
'c' : [MyModel4, MyModel5]
}
I want to design a function that would return me this : [MyModel1, MyModel2,......,MyModel5] . I can easily do the same by iterating over values in the map and then use a nested loop to iterate over each value to finally extract each of the elements. However, what I want is a better way to do it (probably without using the two for loops as my Map can get pretty long at times.
Is there a better way to do it ?
You could use a Collection for and the ... spread operator, like this:
void main() {
Map<String, List<int>> sampleData = {
'a': [1, 2],
'b': [3],
'c': [4, 5, 6]
};
final output = mergeMapValues(sampleData);
print(output); // [1, 2, 3, 4, 5, 6]
}
// Merge all Map values into a single list
List<int> mergeMapValues(Map<String, List<int>> sampleData) {
final merged = <int>[for (var value in sampleData.values) ...value]; // Collection for
return merged;
}
Here's the above sample in Dartpad: https://dartpad.dev/?id=5b4d258bdf3f9468abbb43f7929f4b73

Python: referring to each duplicate item in a list by unique index

I am trying to extract particular lines from txt output file. The lines I am interested in are few lines above and few below the key_string that I am using to search through the results. The key string is the same for each results.
fi = open('Inputfile.txt')
fo = open('Outputfile.txt', 'a')
lines = fi.readlines()
filtered_list=[]
for item in lines:
if item.startswith("key string"):
filtered_list.append(lines[lines.index(item)-2])
filtered_list.append(lines[lines.index(item)+6])
filtered_list.append(lines[lines.index(item)+10])
filtered_list.append(lines[lines.index(item)+11])
fo.writelines(filtered_list)
fi.close()
fo.close()
The output file contains the right lines for the first record, but multiplied for every record available. How can I update the indexing so it can read every individual record? I've tried to find the solution but as a novice programmer I was struggling to use enumerate() function or collections package.
First of all, it would probably help if you said what exactly goes wrong with your code (a stack trace, it doesn't work at all, etc). Anyway, here's some thoughts. You can try to divide your problem into subproblems to make it easier to work with. In this case, let's separate finding the relevant lines from collecting them.
First, let's find the indexes of all the relevant lines.
key = "key string"
relevant = []
for i, item in enumerate(lines):
if item.startswith(key):
relevant.append(item)
enumerate is actually quite simple. It takes a list, and returns a sequence of (index, item) pairs. So, enumerate(['a', 'b', 'c']) returns [(0, 'a'), (1, 'b'), (2, 'c')].
What I had written above can be achieved with a list comprehension:
relevant = [i for (i, item) in enumerate(lines) if item.startswith(key)]
So, we have the indexes of the relevant lines. Now, let's collected them. You are interested in the line 2 lines before it and 6 and 10 and 11 lines after it. If your first lines contains the key, then you have a problem – you don't really want lines[-1] – that's the last item! Also, you need to handle the situation in which your offset would take you past the end of the list: otherwise Python will raise an IndexError.
out = []
for r in relevant:
for offset in -2, 6, 10, 11:
index = r + offset
if 0 < index < len(lines):
out.append(lines[index])
You could also catch the IndexError, but that won't save us much typing, as we have to handle negative indexes anyway.
The whole program would look like this:
key = "key string"
with open('Inputfile.txt') as fi:
lines = fi.readlines()
relevant = [i for (i, item) in enumerate(lines) if item.startswith(key)]
out = []
for r in relevant:
for offset in -2, 6, 10, 11:
index = r + offset
if 0 < index < len(lines):
out.append(lines[index])
with open('Outputfile.txt', 'a') as fi:
fi.writelines(out)
To get rid of duplicates you can cast list to set; example:
x=['a','b','a']
y=set(x)
print(y)
will result in:
['a','b']

Python Immutable Tuple - What Am I Doing Wrong?

Apologies if this is obvious but I'm pretty new to Python and I cannot get my head around this problem. In the following code I have populated a tuple with a series of lists and I am trying to create a new list with items from this tuple. I was hoping that the final result will be that test_raw remains unchanged and test_working will look like the following:
[['aa', 1, 2, 99.5, ['bb', 1, 2, 27.2]],
['aa', 5, 5, 74.2, ['bb', 5, 5, 37]]]
However, in the process, I seem to be appending the 'bb' lists to my tuple as well. I thought that once a tuple is constructed, it cannot be changed but obviously not. Any idea what is happening?
test_raw = (['aa',1,2,99.5],
['bb',1,2,27.2],
['aa',5,5,74.2],
['bb',5,5,37])
test_working = []
for i in range(len(test_raw)):
if test_raw[i][0] == "aa":
test_working.append(test_raw[i])
for i in range(len(test_raw)):
if test_raw[i][0] == "bb":
for j in range(len(test_working)):
if test_working[j][1:3] == test_raw[i][1:3]:
test_working[j].append(test_raw[i])
break
print(test_raw)
(['aa', 1, 2, 99.5, ['bb', 1, 2, 27.2]], ['bb', 1, 2, 27.2], ['aa',.....)
You are not appending to the tuple itself, but the lists inside tuple. I won't debug your code for you but when you run your code, you'll notice that your first list (originally ['aa',1,2,99.5]) has a new element in it (['bb', 1, 2, 27.2])
You aren't appending to the tuple, you are just changing the lists that are inside that tuple
Consider this simple example
my_tuple = (1,2,3, [4,5,6])
my_tuple[3].append(7)
This doesn't add onto my_tuple, just the list that is the last element of it

Ordered lua table looping using C API

Consider the following lua table:
foo = {
bar1 = {a = 1, b = 2, c = "hello"},
bar2 = {a = 5, b = 2, c = "bbq"},
bar3 = {a = 76, b = 13, c = "pwnd"}
}
I am trying to iterate this table using the lua C API to retrieve the key names, bar1, bar2 and bar3. I used the lua_next(L, -2) function to iterate as suggested by many, but the problem is that it returns the elements in random order. The order changes on each run.
I use the following code:
for( lua_pushnil(L); lua_next(L, -2) != 0; lua_pop(L, 1) )
{
printf("%s\n", lua_tostring(L, -2));
}
Most of the time, the output is unordered, such as bar2 bar1 bar3. When lucky, it's ordered. Is there an easy way to loop the table keys in an ordered fashion? What would be the equivalent code as I use, but ordered? Thanks!
Edit:
I know I am using a map rather than an array. But in lua script we have ipairs which would work just fine for this case. I'm looking at the C API equivalent. I've found this stackoverflow answer which gives a different answer, but it doesn't work for me, so I wonder if that answer is good or relevant.
No. The order that Lua traverses tables is undefined.
If you know that the keys are of the form barXXX, then you can create these keys as strings dynamically in the order you wish and then get the corresponding value from the table.
Put the names in a C array, and then order them in C.
As others are saying, the order of keys returned by the hash portion of a table is not guaranteed.
Alternatively, use an array-like structure so you can use the array part:
foo = {
{name = 'bar1', a = 1, b = 2, c = "hello"},
{name = 'bar2', a = 5, b = 2, c = "bbq"},
{name = 'bar3', a = 76, b = 13, c = "pwnd"}
}
So you can use ipairs or the equivalent to for i=1,#foo to iterate over the items in order.
Checking Lua's documentation, the main structure that Lua supports is a Hash Table. Given that it's always going to be a hash-table, you'd probably want to reimplement foo as an array. The bar1, bar2, etc can be part of the entry into the array like so:
foo = {}
foo[0] = {name='bar1', ...}
foo[1] = {name='bar2', ...}
...
or as #lhf suggested, just build the bar names inside a for-loop (if you know it's in sequence) and retrieve the values from foo table.

List comparison

I use this question in interviews and I wonder what the best solution is.
Write a Perl sub that takes n lists, and then returns 2^n-1 lists telling you which items are in which lists; that is, which items are only in the first list, the second, list, both the first and second list, and all other combinations of lists. Assume that n is reasonably small (less than 20).
For example:
list_compare([1, 3], [2, 3]);
=> ([1], [2], [3]);
Here, the first result list gives all items that are only in list 1, the second result list gives all items that are only in list 2, and the third result list gives all items that are in both lists.
list_compare([1, 3, 5, 7], [2, 3, 6, 7], [4, 5, 6, 7])
=> ([1], [2], [3], [4], [5], [6], [7])
Here, the first list gives all items that are only in list 1, the second list gives all items that are only in list 2, and the third list gives all items that are in both lists 1 and 2, as in the first example. The fourth list gives all items that are only in list 3, the fifth list gives all items that are only in lists 1 and 3, the sixth list gives all items that are only in lists 2 and 3, and the seventh list gives all items that are in all 3 lists.
I usually give this problem as a follow up to the subset of this problem for n=2.
What is the solution?
Follow-up: The items in the lists are strings. There might be duplicates, but since they are just strings, duplicates should be squashed in the output. Order of the items in the output lists doesn't matter, the order of the lists themselves does.
Your given solution can be simplified quite a bit still.
In the first loop, you can use plain addition since you are only ever ORing with single bits, and you can narrow the scope of $bit by iterating over indices. In the second loop, you can subtract 1 from the index instead of producing an unnecessary 0th output list element that needs to be shifted off, and where you unnecessarily iterate m*n times (where m is the number of output lists and n is the number of unique elements), iterating over the unique elements would reduce the iterations to just n (which is a significant win in typical use cases where m is much larger than n), and would simplify the code.
sub list_compare {
my ( #list ) = #_;
my %dest;
for my $i ( 0 .. $#list ) {
my $bit = 2**$i;
$dest{$_} += $bit for #{ $list[ $i ] };
}
my #output_list;
for my $val ( keys %dest ) {
push #{ $output_list[ $dest{ $val } - 1 ] }, $val;
}
return \#output_list;
}
Note also that once thought of in this way, the result gathering process can be written very concisely with the aid of the List::Part module:
use List::Part;
sub list_compare {
my ( #list ) = #_;
my %dest;
for my $i ( 0 .. $#list ) {
my $bit = 2**$i;
$dest{$_} += $bit for #{ $list[ $i ] };
}
return [ part { $dest{ $_ } - 1 } keys %dest ];
}
But note that list_compare is a terrible name. Something like part_elems_by_membership would be much better. Also, the imprecisions in your question Ben Tilly pointed out need to be rectified.
First of all I would like to note that nohat's answer simply does not work. Try running it, and look at the output in Data::Dumper to verify that.
That said, your question is not well-posed. It looks like you are using sets as arrays. How do you wish to handle duplicates? How do you want to handle complex data structures? What order do you want elements in? For ease I'll assume that the answers are squash duplicates, it is OK to stringify complex data structures, and order does not matter. In that case the following is a perfectly adequate answer:
sub list_compare {
my #lists = #_;
my #answers;
for my $list (#lists) {
my %in_list = map {$_=>1} #$list;
# We have this list.
my #more_answers = [keys %in_list];
for my $answer (#answers) {
push #more_answers, [grep $in_list{$_}, #$answer];
}
push #answers, #more_answers;
}
return #answers;
}
If you want to adjust those assumptions, you'll need to adjust the code. For example not squashing complex data structures and not squashing duplicates can be done with:
sub list_compare {
my #lists = #_;
my #answers;
for my $list (#lists) {
my %in_list = map {$_=>1} #$list;
# We have this list.
my #more_answers = [#$list];
for my $answer (#answers) {
push #more_answers, [grep $in_list{$_}, #$answer];
}
push #answers, #more_answers;
}
return #answers;
}
This is, however, using the stringification of the data structure to check whether things that exist in one exist in another. Relaxing that condition would require somewhat more work.
Here is my solution:
Construct a hash whose keys are the union of all the elements in the input lists, and the values are bit strings, where bit i is set if the element is present in list i. The bit strings are constructed using bitwise or. Then, construct the output lists by iterating over the keys of the hash, adding keys to the associated output list.
sub list_compare {
my (#lists) = #_;
my %compare;
my $bit = 1;
foreach my $list (#lists) {
$compare{$_} |= $bit foreach #$list;
$bit *= 2; # shift over one bit
}
my #output_lists;
foreach my $item (keys %compare) {
push #{ $output_lists[ $compare{$item} - 1 ] }, $item;
}
return \#output_lists;
}
Updated to include the inverted output list generation suggested by Aristotle