Understanding LINQ subquery - c#

I used a normal query inside a foreach to reach my goal, but I think there is a better way.
int max = 0;
foreach(Area area in myZoo.AreaList)
{
max = (from a in area.AnimalList
select a.ID).Max();
}
return max;
How can I use a pure LINQ to get the max of all sublists? (Animal is a List of the class Animal inside the AreaList and contains an Int32 called ID) Erm, and Lambda is welcome, so don't hastitate to answer only because you only know the lambda answer ;)
public class Zoo
{
public List<Area> AreaList {get; set;}
}
public class Area
{
public List<Animal> AnimalList {get; set;}
}
public class Animal
{
public List<int> Id {get; set;}
}
Only the short form so no one gets confused ;)

Although SelectMany works, I feel query is more readable in such case.
var max = (from area in myZoo.AreaList
from animal in area.AnimalList
from id in animal.Id
select id).Max();

You are looking for a nested SelectMany.
SelectMany will return a single IEnumerable<T> from the many "inner" Ienumerable<T> - so Zoo.SelectMany(a => a.AreaList) will return a single IEnumerable<Area> containing all the IEnumerable<Area> from the Area property - then you do it one more time for the List<Animal> in the Area class:
Sample data:
var zoo = new Zoo() {
AreaList = new List<Area>()
{
new Area()
{
AnimalList = new List<Animal>()
{
new Animal() {Id = new List<int>() {1, 2, 3}},
new Animal() {Id = new List<int>() {4, 5, 6}}
}
},
new Area()
{
AnimalList = new List<Animal>()
{
new Animal() {Id = new List<int>() {7, 8, 9}},
new Animal() {Id = new List<int>() {10, 11}}
}
},
}
};
Linq query:
var max = zoo.AreaList.SelectMany(a => a.AnimalList).SelectMany(a => a.Id).Max();
Console.WriteLine(max);
Result: 11
In query syntax you can do SelectMany by chaining from clauses, as shown in gxp's answer. (Personally, I prefer the method chaining syntax, so it took me some time to figure that one out...)

var max = zoo.AreaList.Max(arl => arl.AnimalList.Max(anl => anl.Id)).Max();
The maximum value of all maximum values of all AnimalLists, and the Max of them.
That's effectievly the same as ZoharPeled's SelectMany, except he flattens the list and takes the max of all items where I take the max of each list again and again.

Related

Merge Two List and Sort [duplicate]

This question already has answers here:
Joining two lists together
(15 answers)
Closed 2 years ago.
class A
{
int id,
DateTime joinDate,
string name
}
class B
{
int uniqueId,
DateTime joinDate,
}
I have two lists with the above objects
List<A> listofA
List<B> listofB
I want to
merge these two lists into a new list and then sort the new list by DateTime
which is common to both lists.
I have followed the this link to understand the merge logic.
Kindly do provide a feasible solution to understand such sorting logic
So far the result
List<object> mergedList = (from x in A select (Object)x).ToList();
mergedList.AddRange((from x in B select (Object)x).ToList());
mergedList.Orderby()??? based on joinDate
Something like this could work:
var m = A.Select(a => new { a.JoinDate, O = (object)a })
.Concat(B.Select(b => new { b.JoinDate, O = (object)b }))
.OrderBy(c => c.JoinDate);
But note that for each item in the list you're then going to have to ask if O is a ClassA or a ClassB
for(var x in m){
if(x.O is ClassA)
...
}
I do think the suggestions in the comments that these classes should descend from a common ancestor or implement a common interface would make them easier to work with..
As noted you can't merge two disparate class types without resorting to using the object class, which I'd recommend against as it is a potential source of introducing bugs.
Better to give each class a base interface and work with a list based on that.
You then need to compare your property type in order to determine how best to sort it.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
List<ClassA> listA = new List<ClassA>()
{
new ClassA(){Id=1, JoinDate = new DateTime(2013, 6, 1, 12, 32, 30)},
new ClassA(){Id=2, JoinDate = new DateTime(2021, 7, 2, 12, 32, 30)},
};
List<ClassB> listB = new List<ClassB>()
{
new ClassB(){UniqueId=23, JoinDate = new DateTime(2021, 2, 6, 8, 32, 30)},
new ClassB(){UniqueId=47, JoinDate = new DateTime(2017, 2, 6, 8, 32, 30)},
};
List<IJoinDate> listJoinDate = new List<IJoinDate>();
foreach(var item in listA)
{
listJoinDate.Add(item);
}
foreach(var item in listB)
{
listJoinDate.Add(item);
}
listJoinDate.Sort((x,y)=>
{
if (x.JoinDate == null && y.JoinDate == null) return 0;
else if (x.JoinDate == null) return -1;
else if (y.JoinDate == null) return 1;
else return x.JoinDate.CompareTo(y.JoinDate);
});
Console.WriteLine("Total records = %d", listJoinDate.Count);
foreach(var item in listJoinDate)
{
Console.WriteLine(item.JoinDate);
}
Console.WriteLine("Linq OrderByDescending");
listJoinDate.AsQueryable().OrderByDescending(x=>x.JoinDate);
foreach(var item in listJoinDate)
{
Console.WriteLine(item.JoinDate);
}
Console.WriteLine("Linq OrderBy");
listJoinDate.AsQueryable().OrderBy(x=>x.JoinDate);
foreach(var item in listJoinDate)
{
Console.WriteLine(item.JoinDate);
}
}
}
public interface IJoinDate
{
DateTime JoinDate{get;set;}
}
public class ClassA :IJoinDate
{
public int Id{get;set;}
public DateTime JoinDate{get;set;}
}
public class ClassB :IJoinDate
{
public int UniqueId{get;set;}
public DateTime JoinDate{get;set;}
public string Name{get;set;}
}
Populate your lists, listofA and listofB and use following queries.
Order by Ascending
var mergedList = (from elementA in listofA select new { ID = elementA.id, JoinDate = elementA.joinDate, Name = elementA.name })
.Concat(from elementB in listofB select new { ID = elementB.uniqueId, JoinDate = elementB.joinDate, Name = string.Empty })
.OrderBy(x => x.JoinDate);
Order by Descending
var mergedList = (from elementA in listofA select new { ID = elementA.id, JoinDate = elementA.joinDate, Name = elementA.name })
.Concat(from elementB in listofB select new { ID = elementB.uniqueId, JoinDate = elementB.joinDate, Name = string.Empty })
.OrderByDescending(x => x.JoinDate);

Compare Object vs Dictionary<int,List<int>> considering performance c#

using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp5
{
class Validator
{
static void Main()
{
var metaValues = new List<Meta>
{
new Meta(4, 15, true),
new Meta(5, 20, false)
};
var requestDict = new Dictionary<int, List<int>>
{
{4, new List<int>{15,20} },// error not exist
{5, new List<int>{25} }, // error its false
{6, new List<int>{30} } // error not exist
};
var matchedIds = new List<int>();
if (metaValues.Any())
{
foreach (var ob in metaValues)
{
if (requestDict.ContainsKey(ob.Id))
{
matchedIds.Add(ob.Id);
var valuesDict = requestDict[ob.Id];
//here i cant get all the values and its Active of meta.Id
}
}
}
foreach (var key in requestDict.Keys)
{
if (!matchedIds.Contains(key))
Console.WriteLine("Invalid");
}
}
}
public class Meta
{
public int Id { get; private set; }
public int Value { get; private set; }
public bool IsActive { get; private set; }
public Meta(int id, int value, bool isActive)
{
Id = id;
Value = value;
IsActive = isActive;
}
}
}
iterating dictionary with object causing performance issue since everytime dictionary key has to be iterated in an list of object so i am trying to take object and lookup in dictionary on below condition
Invalid when meta.Id does not exist in dictionary key
Invalid when one of the meta.Value does not exist in dictionary values List
Inactive when meta.Id and meta.value match with dictionary but meta.isactive is false
I probably shouldn't bother answering since:
The code is quite messy
It does not compile
The question is very unclear
However, for some reason I feel like I understand a little what you're trying to do and wanted to provide some help.
First, let's NOT name a class with the same name as a built-in type (System.Object). Perhaps Item is generic enough? Also, you appear to instantiate instances of this class by calling a constructor that doesn't exist, so let's add that constructor as well:
public class Item
{
public int Id { get; }
public int Value { get; }
public bool IsActive { get; }
public Item(int id, int value, bool isActive)
{
Id = id;
Value = value;
IsActive = isActive;
}
}
Now we can create our list of Item objects by calling the constructor:
var items = new List<Item>
{
new Item(4, 15, true),
new Item(5, 20, false)
};
It also appears that you're creating a dictionary that contains a Key of type int that maps to Item.Id, and a Value of type List<int> that sort-of maps to Item.Value (though Item.Value is a single int). A problem in the code you posted is that you're trying to add two items with the same Key value of 4, which is not legal for a Dictionary - all the keys must be unique. To fix this, I'm using unique keys:
var requests = new Dictionary<int, List<int>>
{
{4, new List<int> {15}},
{5, new List<int> {20}},
{6, new List<int> {25}},
{7, new List<int> {30}}
};
Next it appears that you're trying to create a List<int> of numbers representing the Item.Id values that exist as dictionary keys. This can be done with a System.Linq extension method:
var matchedIds = items
.Where(item => requests.ContainsKey(item.Id))
.ToList();
And finally, it's not exactly clear what you want to do with this list, but it appears you want to do something if either an Item.Id does not exist in the dictionary, or the Item.Id exists but the Item.Value is not in the list, or the item does exist, but the Item.IsActive value is false, or some other combination of these properties.
Here's how to get those items:
var matchedIds = items
.Where(item => requests.ContainsKey(item.Id))
.ToList();
var matchedIdsAndValues = matchedIds
.Where(item => requests[item.Id].Contains(item.Value))
.ToList();
var matchedIdsMissingValue = matchedIds
.Where(item => !requests[item.Id].Contains(item.Value))
.ToList();
var unmatchedIds = items
.Where(item => !requests.ContainsKey(item.Id))
.ToList();
var matchedIdAndValueButNotActive = matchedIdsAndValues
.Where(item => !item.IsActive)
.ToList();
Hope this helps!

Could add multiple data type of data into one list by one code??? C#

I try to add multiple data type of data in one list.
public class DataList
{
public List<int> DataOne=new List<int>();
public int DataTwo;
public double DataThree;
public List<int> DataFour = new List<int>();
public DataList(List<int> DataOne, int DataTwo, double DataThree, List<int> DataFour)
{
this.DataOne=DataOne;
this.DataTwo=DataTwo;
this.DataThree=DataThree;
this.DataFour=DataFour;
}
}
public List<DataList> AddAllData = new List<final>();
AddAllData.Add(new DataList( ????? )); //<-Could add multiple data by one code?
thank you Guy solve this.
But
for more: if
public class One
{
public List<int> OneLise=new List<int>();
public List<double> TwoLise=new List<int>();
public One(List<int> OneLise, int TwoLise)
{
this.OneLise=OneLise;
this.TwoLise=TwoLise;
}
}
public class DataList
{
public List<One> DataOne=new List<One>();
public int DataTwo;
public DataList(List<One> DataOne, int DataTwo)
{
this.DataOne=DataOne;
this.DataTwo=DataTwo;
}
}
AddAllData.Add(new DataList( ????? )); //<-Could add multiple data by one code?
e.g:something like that
AddAllData.Add(new DataList(DataOne.Add(5,2.3), 1, 5.3, DataFour.Add(4,2.65)));
I know one of method:
public List<one> one = new List<one>();
one.Add(5,2.3);
public List<one> four = new List<one>();
four.Add(4,2.65);
AddAllData.Add(new DataList(one, 1, 5.3, four));
but it need more code to add data into list.
if AddAllData is more that 1000 element. it is waste time to add data
How to add multiple data type of data into one list by one code?
You have to write the parameters to all the DataList manually or read them from file. But you can initialize AddAllData with DataList objects like this
// 3 DataLists example
List<DataList> AddAllData = new List<DataList>
{
new DataList(new List<int> {5}, 1, 5.3, new List<int> {4}),
new DataList(new List<int> {7}, 6, 4.3, new List<int> {9}),
new DataList(new List<int> {3, 5}, 4, 5.3, new List<int> {4, 6, 7})
};

Linq Count Expression

Suppose the following Employee class (yes I know I shouldn't publicly expose Lists but this is just an example):
class Employee
{
public string Name {get; set;}
public List<string> Skills {get; set;}
}
Skills is just a list of skills the employee has, for example "programming", "customer service", etc.
Now suppose I have a List<Employee> CurrentEmployees, and I also have another employee, Employee NewHire that is not in the CurrentEmployees list.
My goal is to use a lambda expression to count how many employees in CurrentEmployees have at least one skill that NewHire also has. So for example, if CurrentEmployees contains one employee with Skills{'Programming', 'Troubleshooting'}, and another employee with Skills{'Accounting','Finance'}, and NewHire has Skills{'Programming','Networking'}, I would want an expression that returns 1, because the first employee in the list also has 'Programming' as a skill... is this possible?
Thanks for any help.
currentEmployees.Count(ce =>
ce.Skills.Intersect(newHire.Skills).Any())
var currentEmployees = new List<Employee>
{
new Employee { Skills = new List<string> { "Programming", "Troubleshooting" } },
new Employee { Skills = new List<string> { "Accounting", "Finance" } },
};
var newHire = new Employee { Skills = new List<string> { "Programming", "Networking" } };
var count = currentEmployees.Count(e => e.Skills.Any(newHire.Skills.Contains));
// count == 1
If performance was important, I would use a HashSet<string>:
var newHireSkills = new HashSet<string>(newHire.Skills);
var count = currentEmployees.Count(e => e.Skills.Any(s => newHireSkills.Contains(s)));

Concise way to initialise a collection

I can initialise a class in a concise manner using something like:
public static readonly type TYPE_NAME = new type()
{
ClientIp = "ClientIp",
LanguageCode = "LanguageCode",
SessionToken = "SessionToken",
SystemKey = "SystemKey"
};
However is it possible to initialise a collection in a similar way (inherited from List<>)?
List<string> strList = new List<string>{ "foo", "bar" };
List<Person> people = new List<Person>{
new Person { Name = "Pete", Age = 12},
new Person { Name = "Jim", Age = 15}
};
Use a collection initializer
http://msdn.microsoft.com/en-us/library/bb384062.aspx
List<int> list = new List<int> { 1, 2, 3 };
Yes:
var l = new List<int>() { 1, 1, 2, 3, 5 };
You can surely use collection initializer.
To use this,
List<int> collection= List<int>{1,2,3,...};
To use collection initializer it need not to be exactly of List type.
Collection initializer can be used on those types that implements IEnumerable and has one public Add method.
You use Collection initializer feature even in the following type.
public class SomeUnUsefulClass:IEnumerable
{
public IEnumerator GetEnumerator()
{
throw new NotImplementedException();
}
public void Add(int i)
{
//It does not do anything.
}
}
Like,
SomeUnUsefulClass cls=new SomeUnUsefulClass(){1,2,3,4,5};
which is perfectly valid.

Categories

Resources