LuaInterface: add a table to the script scope - lua

Question: how can I insert a table from C# into 'LuaInterface' script scope using a C# object (preferably anonymous type)?
/// I want to do this, but it does not work
/// (complains that 'test' is userdata and not table
/// when I pass it to pairs() in the script)
//lua["test"] = new { A = 1, B = 2 };
/// another option
/// but building this string is a PITA (actual string is nested and long).
lua.DoString("test = { A = 1, B = 2 }");
// So I have to do this
lua.NewTable("test");
((LuaTable) lua["test"])["A"] = 1;
((LuaTable) lua["test"])["B"] = 2;
lua.DoString("for k,v in pairs(test) do print(k..': '..v) end");

You could fill a C# Dictionary with the keys and values you want to put inside the table. Then do what you're doing in the "I have to..." section, but inside a foreach loop.
Untested code:
var test = new Dictionary<string, int> {
{ "A", 1 },
{ "B", 2 }
};
foreach (var entry in test)
{
((LuaTable) lua]["test"])[entry.Key] = entry.Value;
}
I'd refactor this basic idea into a generic class for added flexibility.

I think if you want to serialize anonymous types into lua tables you will need to user reflection. Maybe you can try to write a lua table serializer. I think I would try to assemble my tables as string and pass it to Lua with DoString
I think the dictionary solution is good and you can use nested tables with without reflection. I tried Tuples, but they are not generic enough and eventually I fell back to the reflection idea.
I would create an extension method:
public static class LuaExt
{
public static LuaTable GetTable(this Lua lua, string tableName)
{
return lua[tableName] as LuaTable;
}
public static LuaTable CreateTable(this Lua lua, string tableName)
{
lua.NewTable(tableName);
return lua.GetTable(tableName);
}
public static LuaTable CreateTable(this Lua lua)
{
lua.NewTable("my");
return lua.GetTable("my");
}
}
Then I could write something like this:
var lua = new Lua();
var table = lua.CreateTable("test");
table["A"] = 1;
table["B"] = 1;
table["C"] = lua.CreateTable();
((LuaTable) table["C"])["A"] = 3;
table["D"] = lua.CreateTable();
((LuaTable)table["D"])["A"] = 3;
foreach (var v in table.Keys)
{
Console.WriteLine(v + ":" + table[v]);
}

Related

Dynamic wheres using TypeORM

I need to generate a query that looks something like:
SELECT v.* FROM versions v
WHERE
(v.id1 = 1 AND v.id2 = 1) OR
(v.id1 = 3 AND v.id2 = 2) OR
(v.id1 = 5 AND v.id2 = 6) OR ...
The parameter to my function contains a list of these paired ids. Unfortunately, id1 and id2 are not the primary keys of this table so I can't use .whereIdsIn().
I am aware of the Brackets object but can't for the life of me figure out how to create a dynamic number of them.
Since you put a generic query, I'll try to answer this generally. You can do something like this:
async foo(tupleList: [number, number][]) {
const builder = createQueryBuilder(Version, 'v');
for (const tuple of tupleList) {
builder.orWhere(new Brackets(qb => {
qb.where("v.id1 = :id1", { id1: tuple[0] })
.andWhere("v.id2 = :id2", { id2: tuple[1] })
}));
}
const result = await builder.getMany();
}

dapper querymultiple spliton error

I'm using: ASP.NET MVC, MySql, Dapper.NET micro-orm
I made a stored procedure with 3 SELECTs, two of which returns lists and the third one returns an integer.
Here is my code:
using (var conn = new MySqlConnection(GetConnectionString()))
{
var readDb = conn.QueryMultiple(storedProcedure, parameters, commandType: CommandType.StoredProcedure);
var result = new someView
{
TopicsList = readDb.Read<ITopic>().ToList(),
TopTopicsList = readDb.Read<IMessage>().ToList(),
TopicsCount = readDb.Read<int>().Single()
};
return result;
}
In ITopic I have TopicId, in IMessage I have MessageId.
And here's the error:
When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id Parameter name: splitOn
I tried adding splitOn on both QueryMultiple and Read, and nigher accepted it.
Though I dont understand why I need splitOn? can't dapper see that I have three separate SELECTs? When using conn.Read(storedProcedure,parameters) on each of the selects separately (instead of MultipleQuery on all of the together) dapper has no problem mapping it to a given object.
What am I doing wrong?
1) Problem solved when I used the real models names instead of their interfaces names:
TopicView instead of ITopic, TopTopicsView instead of IMessage;
2) Once that was fixed and there was no longer "no splitOn" error, started another problem with the < int > casting in line:
TopicsCount = readDb.Read<int>().Single()
probably mysql doesnt return numbers back as ints?
I tried using decimal, object, dynamic, etc.. with no luck. Eventually fixed it by creating another Model with int property inside that has the same name as the database int parameter and now it works.
3) Here's the final working code:
using (var conn = new MySqlConnection(GetConnectionString()))
{
var parameters = context.MapEntity(query);
var multi = conn.QueryMultiple(storedProcedure, parameters, commandType: System.Data.CommandType.StoredProcedure);
var TopicsList = multi.Read<TopicView>().ToList();
var TopTopicsList = multi.Read<TopTopicsView>().ToList();
var result = multi.Read<HomeView>().Single();
result.TopicsList = TopicsList;
result.TopTopicsList = TopTopicsList;
return result;
}

How can I use a HashMap of List of String in Vala?

I am trying to use a HashMap of Lists of strings in Vala, but it seems the object lifecycle is biting me. Here is my current code:
public class MyClass : CodeVisitor {
GLib.HashTable<string, GLib.List<string>> generic_classes = new GLib.HashTable<string, GLib.List<string>> (str_hash, str_equal);
public override void visit_data_type(DataType d) {
string c = ...
string s = ...
if (! this.generic_classes.contains(c)) {
this.generic_classes.insert(c, new GLib.List<string>());
}
unowned GLib.List<string> l = this.generic_classes.lookup(c);
bool is_dup = false;
foreach(unowned string ss in l) {
if (s == ss) {
is_dup = true;
}
}
if ( ! is_dup) {
l.append(s);
}
}
Note that I am adding a string value into the list associated with a string key. If the list doesn't exist, I create it.
Lets say I run the code with the same values of c and s three times. Based some printf debugging, it seems that only one list is created, yet each time it is empty. I'd expect the list of have size 0, then 1, and then 1. Am I doing something wrong when it comes to the Vala memory management/reference counting?
GLib.List is the problem here. Most operations on GLib.List and GLib.SList return a modified pointer, but the value in the hash table isn't modified. It's a bit hard to explain why that is a problem, and why GLib works that way, without diving down into the C. You have a few choices here.
Use one of the containers in libgee which support multiple values with the same key, like Gee.MultiMap. If you're working on something in the Vala compiler (which I'm guessing you are, as you're subclassing CodeVisitor), this isn't an option because the internal copy of gee Vala ships with doesn't include MultiMap.
Replace the GLib.List instances in the hash table. Unfortunately this is likely going to mean copying the whole list every time, and even then getting the memory management right would be a bit tricky, so I would avoid it if I were you.
Use something other than GLib.List. This is the way I would go if I were you.
Edit: I recently added GLib.GenericSet to Vala as an alternative binding for GHashTable, so the best solution now would be to use GLib.HashTable<string, GLib.GenericSet<string>>, assuming you're okay with depending on vala >= 0.26.
If I were you, I would use GLib.HashTable<string, GLib.HashTable<unowned string, string>>:
private static int main (string[] args) {
GLib.HashTable<string, GLib.HashTable<unowned string, string>> generic_classes =
new GLib.HashTable<string, GLib.HashTable<unowned string, string>> (GLib.str_hash, GLib.str_equal);
for (int i = 0 ; i < 3 ; i++) {
string c = "foo";
string s = i.to_string ();
unowned GLib.HashTable<unowned string, string>? inner_set = generic_classes[c];
stdout.printf ("Inserting <%s, %s>, ", c, s);
if (inner_set == null) {
var v = new GLib.HashTable<unowned string, string> (GLib.str_hash, GLib.str_equal);
inner_set = v;
generic_classes.insert ((owned) c, (owned) v);
}
inner_set.insert (s, (owned) s);
stdout.printf ("container now holds:\n");
generic_classes.foreach ((k, v) => {
stdout.printf ("\t%s:\n", k);
v.foreach ((ik, iv) => {
stdout.printf ("\t\t%s\n", iv);
});
});
}
return 0;
}
It may seem hackish to have a hash table with the key and value having the same value, but this is actually a common pattern in C as well, and specifically supported by GLib's hash table implementation.
Moral of the story: don't use GLib.List or GLib.SList unless you really know what you're doing, and even then it's generally best to avoid them. TBH we probably would have marked them as deprecated in Vala long ago if it weren't for the fact that they're very common when working with C APIs.
Vala's new can be a little weird when used as a parameter. I would recommend assigning the new list to a temporary, adding it to the list, then letting it go out of scope.
I would also recommend using libgee. It has better handling of generics than GLib.List and GLib.HashTable.

Default node type for Node<T>

I am starting to investigate the use of Neo4j using the neo4client API.
I have created a basic database, and can query it using the web client. I am now trying to build a sample C# interface. I am having some problems with index lookups. My database consists of nodes with two properties: conceptID and fullySpecifiedName. Auto-indexing is enabled, and both node properties are listed in the node_keys_indexable property of neo4j.properties.
I keep getting IntelliSense errors in my C# when using the Node class. It appears to be defined as Node<T>, but I don't know what to supply as the value of the type. Consider this example from this forum...
var result = _graphClient
.Cypher
.Start(new
{
n = Node.ByIndexLookup("index_name", "key_name", "Key_value")
})
.Return((n) => new
{
N = n.Node<Item>()
})
.Results
.Single();
var n = result.N;
Where does the "Item" in Node<Item> come from?
I have deduced that the index name I should use is node_auto_index, but I can't figure out a default node type.
Item is the type of node you have stored in the DB, so if you have you're storing a class:
public class MyType { public int conceptId { get; set; } public string fullySpecifiedName { get;set; } }
You would be retrieving Node<MyType> back.
Simple flow:
//Store a 'MyType'
_graphClient.Create(new MyType{conceptId = 1, fullySpecifiedName = "Name");
//Query MyType by Index
var query =
_graphClient.Cypher
.Start(new { n = Node.ByIndexLookup("node_auto_index", "conceptId", 1)
.Return<Node<MyType>>("n");
Node<MyType> result = query.Results.Single();
//Get the MyType instance
MyType myType = result.Data;
You can bypass the result.Data step by doing .Return<MyType>("n") instead of Node<MyType> as you'll just get an instance of MyType in that case.

Sorting list or getAll in Grails

I wanted to implement a default sort order in my domain class and immediately found it didn't work with the getAll method. No biggie, I just used list instead. The thing is that the default sort order in a domain class does not allow you specify multiple sort fields (as seen here).
My goal is to sort all Foo objects first by the name of their Bar object, then by their own name.
class Foo {
String name
String Bar
}
class Bar {
String name
}
How can I implement this in the domain class so I don't have to specify a long/nasty comparator every time I call .list()?
One of my attempts:
static Comparator getComparator() {
def c = { a, b ->
def result = a.bar.name.compareTo( b.bar.name );
if ( result == 0 ) {
result = a.name.compareTo( b.name );
}
}
return c as Comparator
}
Then I could just call Foo.list(Foo.getComparator())... if I could get it to work.
Update:
I think I am really close here, just having trouble with implementing two comparisons in the same sort closure.
Foo.list().sort{ a, b ->
def result = a.bar.name <=> b.bar.name;
// Things mess up when I put this if statement in.
if( result == 0 ) {
a.name <=> b.name
}
}
Disco!
class Foo { // My domain class
// ...
static Comparator getComparator() {
def c =[
compare: { a, b ->
def result = a.bar.name <=> b.bar.name;
if( result == 0 ) {
result = a.name <=> b.name
}
return result
}
] as Comparator
}
// ...
}
And implemented like this in my controller:
Foo.list().sort( Foo.getComparator() )
PS:
The above works, but Jeff Storey posted some code in his answer after I discoed, and his code works and is much nicer than mine so use it :)
In your case, would it make sense to have Foo implement Comparable and the implementation could do the comparison as you described? Then when you sort the objects in a list, because they are Comparable, they will sort properly.
If it does not make sense for you to implement Comparable though, you will need to specify a comparator to sort by.
Here's some sample code based on your comments:
edit:
class Person implements Comparable<Person> {
String firstName
String lastName
int compareTo(Person other) {
int lastNameCompare = lastName <=> other.lastName
return lastNameCompare != 0 ? lastNameCompare : firstName <=> other.firstName
}
String toString() {
"${lastName},${firstName}"
}
}
def people = [new Person(firstName:"John",lastName:"Smith"), new Person(firstName:"Bill",lastName:"Jones"), new Person(firstName:"Adam",lastName:"Smith")]
println "unsorted = ${people}"
println "sorted = ${people.sort()}"
This prints:
unsorted = [Smith,John, Jones,Bill, Smith,Adam]
sorted = [Jones,Bill, Smith,Adam, Smith,John]
To further simplify the above post (I would have commented on it but I don't have the rep yet), you can chain the groovy compare operators using the elvis operator:
class Person implements Comparable<Person> {
String firstName
String lastName
int compareTo(Person other) {
return lastName <=> other.lastName ?: firstName <=> other.firstName
}
String toString() {
"${lastName},${firstName}"
}
}
def people = [new Person(firstName:"John",lastName:"Smith"), new Person(firstName:"Bill",lastName:"Jones"), new Person(firstName:"Adam",lastName:"Smith")]
println "unsorted = ${people}"
println "sorted = ${people.sort()}"
This will give you the same result because 0 is considered false in groovy's eyes, which will make it look at the next conditional in the chain.

Resources