Let's say we have the following code:
class Person {
dynamic var name: String!
dynamic var age: Int = 30
}
let Bob = Person()
Bob.name = "Bob"
Bob.age = 60
println("\(Bob.name) is \(Bob.age) years old!") // Outputs Line1, seen below
let predicate = NSPredicate(format: "name = %#", "Bob")
let AClone = Person.objectsWithPredcate(predicate) // Searching for Person objects with name = Bob
println(AClone) // Outputs the Bob object
println(AClone.name) // Has Error1, seen below
The outputs are:
Line1: Bob is 60 years old!
The Errors are:
Error1: does not have a member named 'name'
This error is caused because technically AClone does not have a member named 'name' until after run time. Once the code has been run, AClone then has a member named 'name'. My question is, how do I tell Xcode that .name is a property that AClone will have after run-time? Is there a way for me to print AClone.name?
Note:
I have limited access to many sites due to my location in the world. Please excuse me if an answer to this already exists. If it does, please link to it and as soon as I can I'll take a look at the site.
Thanks!
Related
This question already has an answer here:
Can I reuse code for selecting a custom DTO object for a child property with EF Core?
(1 answer)
Closed 1 year ago.
I have a rather theoretical issue with Entity Framework Core on SQLite.
I have an entity - Person { ID, FirstName, LastName, ... }, class PersonReference { ID, Representation : string }, extension method with argument of type Person that composes reference out of Person like this:
public static PersonReference ComposeReference(this Person from) => new PersonReference
{
ID = from.ID,
Representation = from.FirstName + " " + from.LastName
};
I need to compose references on the sql side. So I do the following:
var result = dbContext.People.Select(p => p.ComposeReference());
Result is IQueriable and program goes beyond that line and materializes the collection successfully. But when I look at the query I see it selects everything of Person and then query text ends.
If I rewrite EF expression to direct
var result = dbContext.People.Select(p => new PersonReference
{
ID = from.ID,
Representation = from.FirstName + " " + from.LastName
});
it gives me the satisfying expression with compact select and string concatenations.
Is there a way to keep the composition logic in extension method but still do calculations on the SQL side?
The trick is about using System.Linq.Expressions.Expression.
I met it at work and didn't get what it was for at first, but it is designed right for the purpose I required.
Declaration:
Expression<Func<Person, PersonReference>> ComposeReference => from => new
PersonReference
{
ID = from.ID,
Representation = from.FirstName + " " + from.LastName
};
Usage:
var result = dbContext.People.Select(ComposeReference);
Pay attention, that expressions can be compiled, but for this case never do it, or it will treat your DbSet as IEnumerable.
The answer from Svyatoslav's comment referred to some libraries, but I think vanilla EF does well enough on its own.
I have a list of a class with several properties, I want to group by 5 of them and then order the results of the grouping by 5 different properties. The result should be a List().
Lets say (pseudocode-ish)....
public class cls()
string firstSTR {get;set;}
string secondSTR {get;set;}
string thirdSTR {get;set;}
string fourthSTR {get;set;}
string fifthSTR {get;set;}
List<cls> vals = new List<cls>();
var tmp = PopulateList(); // Here I would populate several hundred cls()
var unique = tmp.GroupBy(i => new {
i.firstSTR,
i.secondSTR,
i.thirdSTR,
i.fourthSTR,
i.fifthSTR
}).ToList().OrderBy (i => new {
i.firstSTR,
i.secondSTR,
i.thirdSTR,
i.fourthSTR).ToList();
I have very limited experience with Linq and I am not able to figure out why this does not work. The result of the first group by does not return a list of cls so I am not even sure how I can define what I want to order on. I hope that I have provided enough information to explain what I am trying to do. Thank you in advance.
----- EDIT -------
Here is what some example data might look like. I have included 10 columns of sample data however the there are more like 50 columns in the class.
Last5|First5|Add5|ZipCode|InsuranceName|Policy#|Group#|GroupName|SubscriberName|SubscriberAddress
SMITH|JOHN|123 M|99523|Medicare|POL123|GRP123|GRPNM|SMITH| JOHN|123 MAIN ST 99253
SMITH|JOHN|123 M|99523|Medicare|POL123|GRP123|GRPNM|SMITH| JOHN|123 MAIN ST 99253
SMITH|JOHN|123 M|99523|Commercial|POL456|GRP456|GRRNM|SMITH| JOHN|123 MAIN ST 99253
SMITH|MARY|992 W|99324|Medicare|POL789|GRP789|GRPNM|SMITH|MARY|992 WEST ST 99324
SMITH|MARY|992 W|99324|Commerical|POLXXY|GRPXXY|GRPNM|SMITH|MARY|992 WEST ST 99324
Above there are 5 records, delimited with pipe. The output data must be unique on the first 5 fields: Last5, First5, Add5, ZipCode, and Insurance name. Although I am looking to group on these first 5 columns I need all the data in the original class object populated in the result. My result data should look like this.
Last5|First5|Add5|ZipCode|InsuranceName|Policy#|Group#|GroupName|SubscriberName|SubscriberAddress
SMITH|JOHN|123 M|99523|Medicare|POL123|GRP123|GRPNM|SMITH| JOHN|123 MAIN ST 99253
SMITH|JOHN|123 M|99523|Commercial|POL456|GRP456|GRRNM|SMITH| JOHN|123 MAIN ST 99253
SMITH |MARY|992 W|99324|Medicare|POL789|GRP789|GRPNM|SMITH|MARY|992 WEST ST 99324
SMITH |MARY|992 W|99324|Commerical|POLXXY|GRPXXY|GRPNM|SMITH|MARY|992 WEST ST 99324
The result should only have 4 records as one Medicare Records for John Smith has been removed because the first 5 fields are a duplicate of another. Any other field in the remaining 5 columns (again, 50 or so in my data) can be the same, or different, it does not matter.
Im assuming that you don't want aggregates
var unique = tmp.GroupBy(i => new {
i.firstSTR,
i.secondSTR,
i.thirdSTR,
i.fourthSTR,
i.fifthSTR
}).Select(p=> new {
firstSTR = p.Key.firstSTR,
secondSTR = p.Key.secondSTR,
thirdSTR = p.Key.thirdSTR,
fourthSTR= p.Key.fourthSTR,
fifthSTR =p.Key.fifthSTR,
}).OrderBy(p=> p.firstSTR)
.ThenBy(p=> p.secondSTR)
.ThenBy(p=> p.thirdSTR)
.ThenBy(p=> p.fourthSTR)
.ThenBy(p=> p.fifthSTR)
.ToList();
One Gymnast to Many Meets. Can someone provide some info on how I can set meet for each gymnast. It prints each gymnast in array but the meet only gets assigned to last gymnast in array.
newMeet.meetName = "Winter Classic"
newMeet.meetDate = "Sat October 26, 2016"
newMeet.meetDateSort = "2016-09-26"
newMeet.meetTime = "02:00 PM"
newMeet.meetLocation = "Phoenix, AZ"
newMeet.meetStatus = "Active"
let request = NSFetchRequest(entityName: "Gymnast")
request.predicate = NSPredicate(format: "isActive = %#", "Yes")
do {
let results = try AD.managedObjectContext.executeFetchRequest(request) as! [Gymnast]
for result in results {
let newMeets = result.meets?.mutableCopy() as! NSMutableSet
newMeets.addObject(newMeet)
result.meets = newMeets.copy() as? NSSet
print("\(result.fullName!)")
}
} catch {
fatalError("Failed to save data")
}
AD.saveContext()
self.navigationController?.popViewControllerAnimated(true)
You describe the relationship as one Gymnast to many Meets. If it's a one-to-many relationship, that implies that each Meet can only be associated with a single Gymnast, although each Gymnast can be associated with more than one Meet.
If your description of the relationship is correct, then what you're seeing is exactly what would be expected. Since each Meet can only have one Gymnast, each pass through the loop reassigns that relationship from the one set in the previous pass.
I'm guessing that the relationship should be many-to-many, not one-to-many, since each Meet can presumably involve more than one Gymnast.
When is it required to use new in the select clause as in the following example? Thanks.
var results = from person in people
select new { FN = person.FirstName, LN = person.LastName };
I've searched and partially understand this: "In the select new, we're creating a new anonymous type with only the properties you need." But does it mean I can omit new and not get an anonymous type?
To restate my question in another way: is 'new' optional? Is 'new' here similar to C# new where it is not required with value type?
Can I do this?
var results = from person in people
select { FN = person.FirstName, LN = person.LastName };
No, new is not optional if you're selecting "new" objects. (which is what you are doing. You are creating new objects of an anonymous type with 2 properties FN and LN)
Simply omitting new is a syntax error.
If you have an existing type you would like to use, (say you've defined a class Person with 2 properties FN and LN) then you can use that type. E.g.
var results = from person in people
select new Person { FN = person.FirstName, LN = person.LastName };
(assuming Person has a parameterless constructor and FN and LN are properties with setters)
The only time you can omit new is if you are not creating a new object, for instance:
var results = from person in people
select LN = person.LastName;
There you are selecting just the LastName and the result is an IEnumerable<string>
If you are projecting to a new type (anonymous class or known class/struct) it is not optional but required.
If you are projecting to an existing instance though of course you don't need the new, i.e. in your example if you just wanted to project to the first name of each person (to get a string enumeration you could just do:
var results = from person in people select person.FirstName
So rule of thumb if you are constructing a new type you need new in the projection (Select), if you are just selecting something that already exists you don't.
Instead of creating an anonymous type you can instantiate an instance of a named type that takes a Person as an argument or assign values using the object initializer syntax.
Example:
var results = from person in people
select new Report(person);
var results = from person in people
select new Report() { Customer = person };
Edit: Or of course just select a single property from the person object as BrokenGlass pointed out!
Edit: I just re-read your question and wanted to point out that anonymous types in C# are just normal types with the type name generated by the compiler. So yes, you still need to use the new operator because you're creating a new instance of a type. Anonymous Types (C# Programming Guide)
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.