This question is purely academic for me and a spinoff of a question I answered here.
Retrieve object from an arraylist with a specific element value
This guy is using a plain ArrayList... I Know not the best thing to do ... filled with persons
class Person
{
public string Name { get; set; }
public string Gender { get; set; }
public Person(string name, string gender)
{
Name = name;
Gender = gender;
}
}
personArrayList = new ArrayList();
personArrayList.Add(new Person("Koen", "Male"));
personArrayList.Add(new Person("Sheafra", "Female"));
Now he wants to select all females. I solve this like this
var females = from Person P in personArrayList where P.Gender == "Female" select P;
Another guy proposes
var persons = personArrayList.AsQueryable();
var females = persons.Where(p => p.gender.Equals("Female"));
But that does not seem to work because the compiler can never find out the type of p.
Does anyone know what the correct format for my query would be in the second format?
You can use Cast<T> to cast it to a strongly typed enumerable:
var females = personArrayList.Cast<Person>()
.Where(p => p.gender.Equals("Female"));
Cast<T> throws exception if you have anything other than Person in your arraylist. You can use OfType<T> instead of Cast<T> to consider only those objects of type Person.
On a side note, kindly use an enum for gender, not strings.
enum Sex { Male, Female }
class Person
{
public Sex Gender { get; set; }
}
Since the ArrayList has untyped members, you'll have to cast the members to Person:
var females = persons.OfType<Person>().Where(p => p.gender.Equals("Female"));
Cast personArrayList to its element type and you are done.
var persons = personArrayList.Cast<Person>();
Related
Let's say we have a class Student
class Student
{
public string Name { get; set; }
public string Gender { get; set; }
public List<string> Subjects { get; set; }
public static List<Student> GetAllStudetns()
{
List<Student> listStudents = new List<Student>
{
new Student
{
Name = "Tom",
Gender = "Male",
Subjects = new List<string> { "ASP.NET", "C#" }
},
new Student
{
Name = "Mike",
Gender = "Male",
Subjects = new List<string> { "ADO.NET", "C#", "AJAX" }
}
};
return listStudents;
}
}
And we want to print out each student with subjects like this:
Tom - ASP.NET
Tom - C#
Mike - ADO.NET
Mike - C#
etc
so the answer I found is
var result = Student.GetAllStudents().SelectMany(s => s.Subjects, (student, subject) => new { StudentName = student.Name, SubjectName = subject});
//then use foreach loop to retrieve...
I can understand the second use of =>, which is just projection to a anonymous type. But I don't understand the first part
s => s.Subjects, (student, subject)
From my understanding, the left side of => is intput parameter which is Student instance s in this case, but the right side of => should be return type related to the Student instance s,for example, s.Name if we want to get the student's name, so what does (student, subject) mean?
SelectMany is overloaded. See https://msdn.microsoft.com/en-us/library/bb534631(v=vs.110).aspx
s => s.Subjects is your collection selector - the transform that will be applied to each element of the input
and (student, subject) => new { StudentName = student.Name, SubjectName = subject} is your result selector - the transform function to apply to each element of the intermediate sequence
It is because Subjects is defined as a List<string> inside the class and you have to print it as separately, So it needs to iterate over the collection of the sub-list. Before explaining the requested part(s => s.Subjects, (student, subject)) of the query, you should take a look into the definition of the .SelectMany
public static IEnumerable<TResult> SelectMany<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, IEnumerable<TResult>> selector
)
As per the signature of the method, the second parameter(Func) is accepting the source object(here it is of type Student) and an IEnumerable collection objects, here that is of type string, since that is a List<string>.
Which means student in (student, subject) is of type Student and subject will be an item in s.Subjects, So the .SelectMany will further iterates through the sub collection here.
If you want to print like this(Tom - ASP.NET,C#) means you can do GroupBy in your case You don't want to go for that so performs iteration over the sub-list using that code;
Additional information: If the result is based on grouping then your code will be like this:
var result = Student.GetAllStudents()
.GroupBy(s => s.Name)
.Select(x=> new { StudentName = x.Key, SubjectName = String.Join(",",
x.SelectMany(y=>y.Subjects)
.ToList()});
here is code illustration
interface IObjectA
{
int Id { get; }
string Name { get; }
}
class ObjectA : IObjectA
{
public int Id { get; set; }
public string Name { get; set; }
public ObjectA(int id, string name)
{
Id = id;
Name = name;
}
}
There are two ways for me to generate List<IObjectA> from some other objects
First one is using forloop:
IList<IObjectA> list = new List<IObjectA>();
foreach(var item in someList)
{
list.Add(new ObjectA(item.Id, item.Name));
}
This works perfectly fine.
Then I tried with linq
IList<IObjectA> list = someList.Select(c => new ObjectA(c.Id, c.Name)).ToList();
The compiler will throw me a error basically saying cannot convert ObjectA to IObjectA
To make it work, i have to add
IList<IObjectA> list = someList.Select(c => new ObjectA(c.Id, c.Name)).Cast<IObjectA>().ToList();
Can some one explain why the compile would complain?
Thanks in advance!
The problem is that the linq expressions result in a List<ObjectA>. If you can treat this result as a List<IObjectA>, the compiler might let you add hypothetical OtherObjectA objects to the list, which would blow up on you if you ever tried to cast back to the original List<ObjectA> type, which should be allowed.
To get around this, you can .Cast() the elements before calling .ToList() to get a list of the correct type:
IList<IObjectA> list = someList.Select(c => new ObjectA(c.Id, c.Name)).Cast<IObjectA>().ToList();
You could also use the var keyword:
var list = someList.Select(c => new ObjectA(c.Id, c.Name)).ToList();
But this will still result in a List<ObjectA> and I suspect you need the List<IObjectA> for code further on.
How can I box the result of a LINQ select into multiple objects? With the following select clause:
select new {
Person = new Person((String)al.Element("firstName"), (String)al.Element("lastName")),
TimePeriod = new TimePeriod((String)al.Element("start"), (String)al.Element("end"))
};
In the example snippet above, Person and TimePeriod are totally unreleated object. Coming from a Scala background, I would have been happy if the result would be boxed into a tuple. Since I'm new to C#, can anyone help me with this?
If you want to put them into a Tuple you can do this
select Tuple.Create(new Person(...), new TimePeriod(...));
But it would be more advisable to create your own class
public class PersonAndTime
{
public PersonAndTime(Person person, TimePeriod timePeriod)
{
Person = person;
TimePeriod = timePeriod;
}
public Person Person{ get; private set; }
public TimePeriod TimePeriod {get; private set; }
}
And do this
select new PersonAndTime(new Person(...), new TimePeriod(...));
If you don't need to pass the results of the query into or out of a method then leaving it in the anonymous class should be fine.
So I am new to C#, LINQ, and MVC. I am trying to get a list of Ages, but it says
The specified type member 'Age' is not supported in LINQ to Entities.
Only initializers, entity members, and entity navigation properties
are supported.
For a previous tutorial, they use this exact same logic, except they check a string, not an int (Age). Why is this giving me a fit, and how can I fix it?
public ActionResult SearchIndex(string ageValue, string searchString)
{
if (!string.IsNullOrEmpty(ageValue))
{
var AgeList = new List<string>();
var AgeListQry = from d in db.Actors orderby d.Age select d.Age.ToString();
AgeList.AddRange(AgeListQry.Distinct());
}
// other stuff
}
I want to learn what is going on, so that I can avoid this in the future!
Entity Model code
public class Actor
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime BirthDate { get; set; }
public int Age
{
get {
return (int)(DateTime.Now - BirthDate).TotalDays / 365;
}
}
public decimal NetValue { get; set; }
}
public class ActorDBContext : DbContext
{
public DbSet<Actor> Actors { get; set; }
}
As mentioned in the comments, you can't call ToString() in a Linq to Entities query. Instead do it like this:
var AgeList = new List<string>();
//retrieve as whatever type Age is, no conversion in SQL Server
var AgeListQry = (from d in db.Actors orderby d.Age select d.Age).ToList();
//convert them after the fact, using Linq to Objects
AgeList.AddRange(AgeListQry.Select(a => a.ToString()).Distinct());
EDIT
I saw your latest update that does show that Age is not a database column. You are then required to do something like this (assuming BirthDate is properly mapped):
var AgeList = new List<string>();
//retrieve BirthDate from SQL Server and use ToList() to get it to run immediately
var AgeListQry = (from d in db.Actors orderby d.BirthDate select d.BirthDate).ToList();
//convert them after the fact, using Linq to Objects
AgeList.AddRange(AgeListQry.Select(bd => ((int)(DateTime.Now - bd).TotalDays / 365).ToString()).Distinct());
Linq to Entities maps your expressions to SQL statements and there is nothing for it to map to when you use your Age property. Instead, you need to get what you can from SQL Server (BirthDate) and then do the translation to Age yourself. You could replace the inline code with a method call like this if you'd rather:
AgeList.AddRange(AgeListQry.Select(bd => CalculateAge(bd)).Distinct());
//...
private string CalculateAge(DateTime birthday)
{
return ((int)(DateTime.Now - bd).TotalDays / 365).ToString();
}
You haven't the Age in you DB scheme and it is impossible to convert LINQ to DB query.
You must order the Age collection in client side or add calculated column to your table.
There is another way. Have a converter file, where you pass the object, works with the birthdate and produces the age, returns the same object. That also means, that you can't search the database for the age column
I cannot solve a problem for several hours now.
Here is a simplified scenario.
Let's say there is a list of people with their bids. I'm trying to find a person with the highest bid and return the name. I am able to find the highest bid, but how to I output the name?
List<String[]> list = new List<String[]>();
String[] Bob = { "Alice", "19.15" };
String[] Alice = {"Bob", "28.20"};
String[] Michael = { "Michael", "25.12" };
list.Add(Bob);
list.Add(Alice);
list.Add(Michael);
String result = list.Max(s => Double.Parse(s.ElementAt(1))).ToString();
System.Console.WriteLine(result);
As a result I get 28.20, which is correct, but I need to display "Bob" instead. There were so many combinations with list.Select(), but no success. Anyone please?
The best solution from an architectural point of view is to create a separate class (e.g. Person) that contains two properties Name and Bid of each person and a class Persons that contains the list of persons.
Then you can easily use a LINQ command.
Also instead of storing bids as string, think if bids as floating point or decimal values would be better (or store it in cents and use an int).
I don't have a compiler by hand so it's a bit out of my head:
public class Person
{
public string Name { get; set; }
public float Bid { get; set; }
public Person(string name, float bid)
{
Debug.AssertTrue(bid > 0.0);
Name = name;
Bid = bid;
}
}
public class Persons : List<Person>
{
public void Fill()
{
Add(new Person("Bob", 19.15));
Add(new Person("Alice" , 28.20));
Add(new Person("Michael", 25.12));
}
}
In your class:
var persons = new Persons();
persons.Fill();
var nameOfHighestBidder = persons.MaxBy(item => item.Bid).Name;
Console.WriteLine(nameOfHighestBidder);
This works in the simple example. Not sure about the real one
var result = list.OrderByDescending(s => Double.Parse(s.ElementAt(1))).First();
You can use Jon Skeet's MaxBy.
For usage you can see this question
e.g. in this case
list.MaxBy(s => Double.Parse(s.ElementAt(1)))[0]
More here
Should work:
var max = list.Max(t => double.Parse(t[1]));
list.First(s => double.Parse(s[1]) == max)[0]; // If list is not empty
After finding result just do as below:
list.First(x=>x[1] == result)[0]