Can I override a Lua table's return value for itself? - syntax

Is it possible for a table, when referenced without a key, to return a particular value rather than a reference to itself?
Let's say I have the following table:
local person = {
name = "Kapulani",
level = 100,
age = 30,
}
In Lua, I can quite easily refer to "person.name", "person.level", or "person.age" and get the values as expected. However, I have certain cases where I may want to just reference "person" and, instead of getting "table: " I'd like to return the value of "person.name" instead.
In other words, I'd like person.x (or person[x]) to return the appropriate entry from the table, but person without a key to return the value of person.name (or person["name"]). Is there a mechanism for this that I haven't been able to find?
I have had no success with metatables, since __index will only apply to cases where the key does not exist. If I put "person" into a separate table, I can come up with:
local true_person = {
... -- as above
}
local env_mt = {
__index = function(t, k)
if k == 'person' then
return true_person
end
end
}
local env = setmetatable( {}, env_mt )
This lets me use __index to do some special handling, except there's no discernable way for me to tell, from __index(), whether I'm getting a request for env.person (where I'd want to return true_person.name) or env.person[key] (where I'd want to return true_person as a table, so that 'key' can be accessed appropriately).
Any thoughts? I can approach this differently, but hoping I can approach this along these lines.

You can do it when the table is being used as a string by setting the __tostring metatable entry:
$ cat st.lua
local person = {
name = "Kapulani",
level = 100,
age = 30,
}
print(person)
print(person.name)
print(person.age)
setmetatable(person, {__tostring = function(t) return t.name end})
print(person)
$ lua st.lua
lua st.lua
table: 0x1e8478e0
Kapulani
30
Kapulani

I am not sure that what you are asking for is a good idea because it flies in the face of compositionality. Usually one would expect the following two programs to do the same thing but you want them to behave differently
print(person.name)
local p = person
print( p.name )
Its also not very clear how assignment would work. person.age = 10 should change the age but person = otherPerson should change the reference to the perrson, not the age.
If you don't care about compositionality and are onyl reading data, then a more direct way to solve the problem is to have a query function that receives the fields encoded in a string
query("person.age") -- 17
query("person.name") -- "hugomg"
query("person") -- 17; query gets to default to whatever it wants.
To keep the syntax more lightweight you can omit the optional parenthesis
q"person.age"
q"person"
Or you can extend the __index metamethod on the global table, _G
setmetattable(_G, { __index = function(self, key) return query(key) end })
print ( person_age ) -- You will need to use "_" instead of "." for the
-- query to be a valid identifier.

Related

Unable to create a constant value - only primitive types or Enumeration types allowed

I have seen some questions related to this Exception here but none made me understand the root cause of the problem. So here we have one more...
var testquery =
((from le in context.LoanEMIs.Include("LoanPmnt")
join lp in context.LoanPmnts on le.Id equals lp.LoanEMIId
where lp.PmntDtTm < date && lp.IsPaid == false
&& le.IsActive == true && lp.Amount > 0
select new ObjGetAllPendingPmntDetails
{
Id = lp.Id,
Table = "LoanEMI",
loanEMIId = lp.LoanEMIId,
Name = le.AcHead,
Ref = SqlFunctions.StringConvert((double)le.FreqId),
PmntDtTm = lp.PmntDtTm,
Amount = lp.Amount,
IsDiscard = lp.IsDiscarded,
DiscardRemarks = lp.DiscardRemarks
}).DefaultIfEmpty(ObjNull));
List<ObjGetAllPendingPmntDetails> test = testquery.ToList();
This query gives the following Exception Message -
Unable to create a constant value of type CashVitae.ObjGetAllPendingPmntDetails. Only primitive types or enumeration types are supported in this context.
I got this Exception after I added the SQL function statement to convert le.FreqId which is a byte to a string as ToString() is not recognized in the LINQ Expression Store.
ObjGetAllPendingPmntDetails is a partial class in my model which is added as it is used too many times in the code to bind data to tables.
It has both IDs as long, 'Amount' as decimal, PmntDtTm as Datetime,IsDiscard as bool and remaining all are string including 'Ref'.
I get no results as currently no data satisfies the condition. While trying to handle null, I added DefaultIfEmpty(ObjNull) and ObjNull has all properties initialized as follows.
ObjGetAllPendingPmntDetails ObjNull = new ObjGetAllPendingPmntDetails()
{ Id = 0, Table = "-", loanEMIId = 0, Name = "-", Ref = "-",
PmntDtTm = Convert.ToDateTime("01-01-1900"),
Amount = 0, IsDiscard = false, DiscardRemarks = "" };
I need this query to work fine as it has Union() called on it with 5 other queries. All returning the same ObjGetAllPendingPmntDetails columns. But there is some problem as this query has no data satisfying the conditions and the Exception Shared Above.
Any suggestions are appreciated as I am unable to understand the root cause of the problem.
#AndrewCoonce is right, the .DefaultIfEmpty(ObjNull) is the culprit here. Entity Framework turns DefaultIfEmpty into something like...
CASE WHEN ([Project1].[C1] IS NULL) THEN #param ELSE [Project1].[Value] END AS [C1]
...but there's no way to coerce an instance of ObjGetAllPendingPmntDetails into something that can take the place of #param, so you get an exception.
If you move the DefaultIfEmpty call to after the ToList it should work correctly (although you'll need to call ToList again after that if you really want a concrete list instance).

At least one one object must implement Icomparable

I am attempting to get unique values in a list of similar value distinguished only by a one element in a pipe delimited string... I keep getting at least one object must implement Icomparable. I don't understand why I keep getting that. I am able to groupBy that value... Why can't I find the max... I guess it is looking for something to compare it with. If I get the integer version will it stop yelling at me? This is the last time I am going to try using LINQ...
var queryResults = PatientList.GroupBy(x => x.Value.Split('|')[1]).Select(x => x.Max());
I know I can get the unique values some other way. I am just having a hard time figuring it out. In that List I know that the string with the highest value amongst its similar brethren is the one that I want to add to the list. How can I do that? I am totally drawing a blank because I have been trying to get this to work in linq for the last few days with no luck...
foreach (XmlNode node in nodeList)
{
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(node.OuterXml);
string popPatInfo = xDoc.SelectSingleNode("./template/elements/element[#name=\"FirstName\"]").Attributes["value"].Value + ", " + xDoc.SelectSingleNode("./template/elements/element[#name=\"LastName\"]").Attributes["value"].Value + " | " + DateTime.Parse(xDoc.SelectSingleNode("./template/elements/element[#name=\"DateOfBirth\"]").Attributes["value"].Value.Split('T')[0]).ToString("dd-MMM-yyyy");
string patientInfo = xDoc.SelectSingleNode("./template/elements/element[#name=\"PatientId\"]").Attributes["value"].Value + "|" + xDoc.SelectSingleNode("./template/elements/element[#name=\"PopulationPatientID\"]").Attributes["enc"].Value;// +"|" + xDoc.SelectSingleNode("./template/elements/element[#name=\"AdminDate\"]").Attributes["value"].Value;
int enc = Int32.Parse(patientInfo.Split('|')[1]);
if (enc > temp)
{
lastEncounter.Add(enc, patientInfo);
temp = enc;
}
//lastEncounter.Add(Int32.Parse(patientInfo.Split('|')[1]));
PatientList.Add( new SelectListItem { Text = popPatInfo, Value = patientInfo });
}
I was thinking about using some kind of temp variable to find out what is the highest value and then add that string to the List. I am totally drawing a blank however...
Here I get the IDs in an anonymous type to make it readable.
var patientEncounters= from patient in PatientList
let PatientID=Int32.Parse(patient.Value.Split('|')[0])
let EncounterID=Int32.Parse(patient.Value.Split('|')[1])
select new { PatientID, EncounterID };
Then we group by UserID and get the last encounter
var lastEncounterForEachUser=from pe in patientEncounters
group pe by pe.PatientID into grouped
select new
{
PatientID=grouped.Key,
LastEncounterID=grouped.Max(g=>g.EncounterID)
};
Linq doesn't know how to compare 2 Patient objects, so it can't determine which one is the "greatest". You need to make the Patient class implement IComparable<Patient>, to define how Patient objects are compared.
// Compare objets by Id value
public int CompareTo(Patient other)
{
return this.Id.CompareTo(other.Id);
}
Another option is to use the MaxBy extension method available in Jon Skeet's MoreLinq project:
var queryResults = PatientList.GroupBy(x => x.Value.Split('|')[1])
.Select(x => x.MaxBy(p => p.Id));
EDIT: I assumed there was a Patient class, but reading your code again, I realize it's not the case. PatientList is actually a collection of SelectListItem, so you need to implement IComparable in that class.

linq 'range variable' problem

I have a strange problem when deleteting records using linq, my suspicion is that it has something to do with the range variable (named source). After deleting a record all targets for a customer are retrieved using the following statement:
var q = from source in unitOfWork.GetRepository<db_Target>().Find()
where source.db_TargetBase.db_Person.fk_Customer == customerID
select source.FromLinq();
where FromLinq is in extention method on db_target:
public static Target FromLinq(this db_Target source)
{
return new Target
{
id = source.id,
LastModified = source.db_TargetBase.LastModified,
...
}
}
When a record is deleted both db_Target and db_TargetBase are deleted. When, for example, two users are deleting records, linq tries to retrieve a record for user2 which is deleted by user1, causing a crash on the LastModified = source.db_TargetBase.LastModified line because db_TargetBase is null.
When using the following code the problem does not occure and only the non-deleted records are retrieved:
var q = from source in unitOfWork.GetRepository<db_Target>().Find()
where source.db_TargetBase.db_Person.fk_Customer == customerID
select new Target
{
id = source.id,
LastModified = source.db_TargetBase.LastModified,
...
};
This spawns two questions:
What is happening here? Am I making a copy of the range variable source because I'm using it in a extention method?
How can I "wrap" the return new Target code? I am using this in multiple places and do not want to copy it every time. Making my code harder to maintain.
TIA,
JJ
In the first set of code - since the initializer lives an a non-translatable method (extension or otherwise), it cannot be translated - so it is run locally.
In the second set of code - the initializer is represented by an elementinit expression, which is translated (examine/compare the select clause of the generated sql for proof).
if you want to wrap this, you need to have an Expression<Func<db_Target, Target>> that anyone can grab and use in thier query. Fortunately, that's easy to do:
public Expression<Func<db_Target, Target>> GetFromLinqExpressionForTarget()
{
return
source => new Target
{
id = source.id,
LastModified = source.db_TargetBase.LastModified,
...
}
}
Which may be used like so:
var FromLinq = GetFromLinqExpressionForTarget();
var q =
(
from source in ...
...
...
select source
).Select(FromLinq);
Now ... I'm really running on a guess here and am only about 60% confident that my answer is correct. So if someone wants to confirm this, that'll make my day. :)

Lua - Sorting a table alphabetically

I have a table that is filled with random content that a user enters. I want my users to be able to rapidly search through this table, and one way of facilitating their search is by sorting the table alphabetically. Originally, the table looked something like this:
myTable = {
Zebra = "black and white",
Apple = "I love them!",
Coin = "25cents"
}
I was able to implement a pairsByKeys() function which allowed me to output the tables contents in alphabetical order, but not to store them that way. Because of the way the searching is setup, the table itself needs to be in alphabetical order.
function pairsByKeys (t, f)
local a = {}
for n in pairs(t) do
table.insert(a, n)
end
table.sort(a, f)
local i = 0 -- iterator variable
local iter = function () -- iterator function
i = i + 1
if a[i] == nil then
return nil
else
return a[i], t[a[i]]
end
end
return iter
end
After a time I came to understand (perhaps incorrectly - you tell me) that non-numerically indexed tables cannot be sorted alphabetically. So then I started thinking of ways around that - one way I thought of is sorting the table and then putting each value into a numerically indexed array, something like below:
myTable = {
[1] = { Apple = "I love them!" },
[2] = { Coin = "25cents" },
[3] = { Zebra = "black and white" },
}
In principle, I feel this should work, but for some reason I am having difficulty with it. My table does not appear to be sorting. Here is the function I use, with the above function, to sort the table:
SortFunc = function ()
local newtbl = {}
local t = {}
for title,value in pairsByKeys(myTable) do
newtbl[title] = value
tinsert(t,newtbl[title])
end
myTable = t
end
myTable still does not end up being sorted. Why?
Lua's table can be hybrid. For numerical keys, starting at 1, it uses a vector and for other keys it uses a hash.
For example, {1="foo", 2="bar", 4="hey", my="name"}
1 & 2, will be placed in a vector, 4 & my will be placed in a hashtable. 4 broke the sequence and that's the reason for including it into the hashtable.
For information on how to sort Lua's table take a look here: 19.3 - Sort
Your new table needs consecutive integer keys and needs values themselves to be tables. So you want something on this order:
SortFunc = function (myTable)
local t = {}
for title,value in pairsByKeys(myTable) do
table.insert(t, { title = title, value = value })
end
myTable = t
return myTable
end
This assumes that pairsByKeys does what I think it does...

LINQ to SQL bug (or very strange feature) when using IQueryable, foreach, and multiple Where

I ran into a scenario where LINQ to SQL acts very strangely. I would like to know if I'm doing something wrong. But I think there is a real possibility that it's a bug.
The code pasted below isn't my real code. It is a simplified version I created for this post, using the Northwind database.
A little background: I have a method that takes an IQueryable of Product and a "filter object" (which I will describe in a minute). It should run some "Where" extension methods on the IQueryable, based on the "filter object", and then return the IQueryable.
The so-called "filter object" is a System.Collections.Generic.List of an anonymous type of this structure: { column = fieldEnum, id = int }
The fieldEnum is an enum of the different columns of the Products table that I would possibly like to use for the filtering.
Instead of explaining further how my code works, it's easier if you just take a look at it. It's simple to follow.
enum filterType { supplier = 1, category }
public IQueryable<Product> getIQueryableProducts()
{
NorthwindDataClassesDataContext db = new NorthwindDataClassesDataContext();
IQueryable<Product> query = db.Products.AsQueryable();
//this section is just for the example. It creates a Generic List of an Anonymous Type
//with two objects. In real life I get the same kind of collection, but it isn't hard coded like here
var filter1 = new { column = filterType.supplier, id = 7 };
var filter2 = new { column = filterType.category, id = 3 };
var filterList = (new[] { filter1 }).ToList();
filterList.Add(filter2);
foreach(var oFilter in filterList)
{
switch (oFilter.column)
{
case filterType.supplier:
query = query.Where(p => p.SupplierID == oFilter.id);
break;
case filterType.category:
query = query.Where(p => p.CategoryID == oFilter.id);
break;
default:
break;
}
}
return query;
}
So here is an example. Let's say the List contains two items of this anonymous type, { column = fieldEnum.Supplier, id = 7 } and { column = fieldEnum.Category, id = 3}.
After running the code above, the underlying SQL query of the IQueryable object should contain:
WHERE SupplierID = 7 AND CategoryID = 3
But in reality, after the code runs the SQL that gets executed is
WHERE SupplierID = 3 AND CategoryID = 3
I tried defining query as a property and setting a breakpoint on the setter, thinking I could catch what's changing it when it shouldn't be. But everything was supposedly fine. So instead I just checked the underlying SQL after every command. I realized that the first Where runs fine, and query stays fine (meaning SupplierID = 7) until right after the foreach loop runs the second time. Right after oFilter becomes the second anonymous type item, and not the first, the 'query' SQL changes to Supplier = 3. So what must be happening here under-the-hood is that instead of just remembering that Supplier should equal 7, LINQ to SQL remembers that Supplier should equal oFilter.id. But oFilter is a name of a single item of a foreach loop, and it means something different after it iterates.
I have only glanced at your question, but I am 90% sure that you should read the first section of On lambdas, capture, and mutability (which includes links to 5 similar SO questions) and all will become clear.
The basic gist of it is that the variable oFilter in your example has been captured in the closure by reference and not by value. That means that once the loop finishes iterating, the variable's reference is to the last one, so the value as evaluated at lambda execution time is the final one as well.
The cure is to insert a new variable inside the foreach loop whose scope is only that iteration rather than the whole loop:
foreach(var oFilter in filterList)
{
var filter = oFilter; // add this
switch (oFilter.column) // this doesn't have to change, but can for consistency
{
case filterType.supplier:
query = query.Where(p => p.SupplierID == filter.id); // use `filter` here
break;
Now each closure is over a different filter variable that is declared anew inside of each loop, and your code will run as expected.
Working as designed. The issue you are confronting is the clash between lexical closure and mutable variables.
What you probably want to do is
foreach(var oFilter in filterList)
{
var o = oFilter;
switch (o.column)
{
case filterType.supplier:
query = query.Where(p => p.SupplierID == o.id);
break;
case filterType.category:
query = query.Where(p => p.CategoryID == o.id);
break;
default:
break;
}
}
When compiled to IL, the variable oFilter is declared once and used multiply. What you need is a variable declared separately for each use of that variable within a closure, which is what o is now there for.
While you're at it, get rid of that bastardized Hungarian notation :P.
I think this is the clearest explanation I've ever seen: http://blogs.msdn.com/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx:
Basically, the problem arises because we specify that the foreach loop is a syntactic sugar for
{
IEnumerator<int> e = ((IEnumerable<int>)values).GetEnumerator();
try
{
int m; // OUTSIDE THE ACTUAL LOOP
while(e.MoveNext())
{
m = (int)(int)e.Current;
funcs.Add(()=>m);
}
}
finally
{
if (e != null) ((IDisposable)e).Dispose();
}
}
If we specified that the expansion was
try
{
while(e.MoveNext())
{
int m; // INSIDE
m = (int)(int)e.Current;
funcs.Add(()=>m);
}
then the code would behave as expected.
The problem is that you're not appending to the query, you're replacing it each time through the foreach statement.
You want something like the PredicateBuilder - http://www.albahari.com/nutshell/predicatebuilder.aspx

Resources