Select using linq based on 2 conditions - c#

I have a collection. I want to select values that matches 2 conditions.
var a;
var b;
foreach(SomeObject so in collection)
{
if(so.Value == something)
{
a = so;
// break if b is not null.
}
else if(so.Value == somethingElse)
{
b = so;
// break if a is not null
}
}
Is it possible to do the above using linq iterating over the collection only once? There will be only one value of something and one value of somethingElse, if we can use that fact.

This solution narrows the list down to two elements and then goes from there. Take(2) is used so the collection only gets searched until the two values are found.
var matches = collection.Where(x => x.Value == something || x.Value == somethingElse)
.Take(2)
.ToList();
var a = matches.Single(x => x.Value == something);
var b = matches.Single(x => x.Value == somethingElse);

var relevant =
from so in l where so.Value == something || so.Value == somethingElse
group so by so.Value == something into grp
select new {ForA = grp.Key, SO = grp.First()};
foreach(var bit in relevant)
if(bit.ForA)
a = bit.SO;
else
b = bit.SO;
It could gain you something against some sources, in that only two items are retrieved, though if against a collection in memory I'd stay with what you had, but adding a catch so that once you'd set both a and b, you stopped looping rather than kept needlessly examining the collection.

You need something like this:
var a = collection.OfType<YourCollectionElementType>().FirstOrDefault(i=>i.Equals(something));
var b = collection.OfType<YourCollectionElementType>().FirstOrDefault(i=>i.Equals(somethingelse));
Your collection should implement IEnumerable at least to be able to use this code.
It depends on what the type of your collection is. If it implements generic IEnumerable<T>, say it's List<YourCollectionElementType> or an array YourCollectionElementType[] then you don't need to use OfType<T>, i.e.
var a = collection.FirstOrDefault(i=>i.Equals(something));
var b = collection.FirstOrDefault(i=>i.Equals(somethingelse));
If your collection doesn't contain that value, a and/or b would get null values.
Actually you can read all these things in MSDN. LINQ is not that hard to learn, if you try
For example:
Enumerable.FirstOrDefault Method (IEnumerable)
Enumerable.OfType Method
EDIT
In your comment you're saying that It is assured that only one value will be present. Is it of great importance that you need two separate variables? You could get the present value just like this:
object thevalue = collection.FirstOrDefault(i => i == something || i == somethingelse);
EDIT
Actually, I'd leave your loop as it is, only having added a line like this:
SomeObject a;
SomeObject b;
foreach(SomeObject so in collection)
{
if(so.Value == something)
a = so;
else if(so.Value == somethingElse)
b = so;
if(a!=null && b!=null)
break;
}
And if only one of the values is expected, then
SomeObject a;
SomeObject b;
foreach(SomeObject so in collection)
{
if(so.Value == something)
{
a = so;
break;
}
else if(so.Value == somethingElse)
{
b = so;
break;
}
}

You could do this:
collection
.Select(x => x.Value == something
? () => a = x
: (x.Value == somethingElse
? () => b = x
: (Action)null))
.Where(x => x != null)
.ToArray()
.ForEach(x => x());
I tested this code and it worked a treat.
If the collection is out on a database somewhere then I suggest this:
collection
.Where(x => x.Value == something || x.Value == somethingElse)
.ToArray()
.Select(x => x.Value == something
? () => a = x
: (x.Value == somethingElse
? () => b = x
: (Action)null))
.Where(x => x != null)
.ToArray()
.ForEach(x => x());

Sure you can, but I wouldn't recommend it. It would be cleaner to have your loop as you already have it now.
If you really wanted to know how, you could do this:
class SomeObject
{
public string Value { get; set; }
}
var items = new[]
{
new SomeObject { Value = "foo" },
new SomeObject { Value = "bar" },
new SomeObject { Value = "baz" },
};
var something = "foo";
var somethingElse = "baz";
var result = items.Aggregate(
Tuple.Create(default(SomeObject), default(SomeObject)),
(acc, item) => Tuple.Create(
item.Value == something ? item : acc.Item1,
item.Value == somethingElse ? item : acc.Item2
)
);
// result.Item1 <- a == new SomeObject { Value = "foo" }
// result.Item2 <- b == new SomeObject { Value = "baz" }

Not sure If I understand your question but assume Something and SomethingElse are value type and known before the run-time, you can do like below in one statement.
var FindItems = new[]
{
new SomeObject { Value = "SomethingElse" }
};
var CollectionItems = new[]
{
new SomeObject { Value = "Something" },
new SomeObject { Value = "SomethingElse" }
};
var qSomeObject = CollectionItems
.Where(c => FindItems.Any(x => c.Value == x.Value));

Related

Return TRUE for ALL when specific property is smaller then other property for both lists

Its hard to explain so I make a simplified data sample here:
I have here 2 lists of different complex type:
list1:
{ Id = 1 , Value = 1 }; {Id = 2 , Value = 2 }; { Id = 3 , Value = 1.5}
list2
{ Id = 1 , Value = 1 }; {Id = 2 , Value = 2 }; { Id = 3 , Value = 1.5}
A comparison of both lists should return TRUE as each value of Value property is equal in both lists.
If just one of the Value properties value differs then the whole result must be FALSE.
How can I do that with linq preferd?
Try this, with LINQ's Zip method:
var result = list1.Zip(list2, (l1, l2) => l1.Value == l2.Value).All(x => x);
If you need to perform this check by Id property, then GroupJoin is what you are looking for.
It lets you group two different collections by selector and then join them:
bool ComplexCollectionValuesAreEqual(List<ComplexItem1> list1, List<ComplexItem2> list2)
{
try
{
var grouped = list1.GroupJoin(list2, x => x.Id, x => x.Id,
(outer, inners) => outer.Value == inners.Single().Value);
return grouped.All(x => x);
}
catch (InvalidOperationException) // for .Single() fail case
{
return false;
}
}
You can apply other comparison logic in the last lambda of GroupJoin, for example outer.Value <= inners.Single().Value to check if all Values in item1 are equal or less than corresponding values in item2.
Note that in this implementation it will return false, if there is no object with such ID in list2 collection. You may want to throw exceptions instead, if you always except it to exist.
bool isTrue = list1.Select((z, i) => z.Id != list2[i].Id || z.Value != list2[i].Value).Count() == 0;
If the collections are not sorted
bool isTrue = list1.Where(x => list2.First(y => y.Id == x.Id).Value != x.Value ).Count() == 0;
If the items should not have duplicates and count should be equal
bool isTrue = list1.Where(x => list2.First(y => y.Id == x.Id).Value == x.Value).Count() == list2.Count && list2.Count == list1.Count;

List.OrderBy define order in a variable

I have a list containing objects :
class MyType
{
String Name;
String Address;
DateTime BirthDay;
}
List<MyType> myList;
I want to write a function working like this :
public void SortList(int value)
{
// orderValue => this is the variable I need
if (value == 0)
orderValue = MyType.Name; // Sort list by Name
else if (value == 1)
orderValue = MyType.Address; // Sort list by Address
else if (value == 1)
orderValue = MyType.BirthDay; // Sort list by BirthDay
if (orderValue != null)
{
List<MyType> sortedList = myList.OrderBy(orderValue).ToList();
if (Enumerable.SequenceEqual(myList, sortedList))
sortedList = myList.OrderByDescending(orderValue).ToList();
myList = sortedList;
}
}
How can I write my orderValue to make this code working ?
This is just a synthesis of my code, I can provide more details if needed.
OrderBy can accept Func and you can have variable like this
Func<Person, string> orderFunc = x => x.Name;
if (value == 0)
orderFunc = x => x.Name;
else if (value == 1)
orderFunc = x => x.Address;
myList = myList.OrderBy(orderFunc).ToList();
You have several ways of doing it. For example, you can add OrderBy clause inside your conditional statement, like this:
IEnumerable<MyType> data = myList;
if (value == 0)
data = data.OrderBy(x => x.Name); // Sort list by Name
else if (value == 1)
data = data.OrderBy(x => x.Address); // Sort list by Address
res = data.ToList();
You can also do it in a single statement:
res = myList
.OrderBy(x => value == 0 ? x.Name : "")
.ThenBy(x => value == 1 ? x.Address : "")
.ToList();
This is somewhat harder to read, but it will do the work on RDBMS side in EF or LINQ to SQL.
You can try
myList.OrderBy((x) => (value == 0 ? x.Name : x.Address));

How do I create and populate a dynamic object using a dynamically built lambda expression

I'm trying to create and populate a dynamic object from a dataset that is only known at run time. In the code below, I create my IEnumerable results from my dataset with some known fields (ID, Primary Data, DisplayOrder, IsActive) and one user-define field (Phone Number) which I won't know at design time, and therefore must be built dynamically. The code below works, but only because I've hard-coded the dynamic field Phone Number. How do I build the Lambda expression dynamically to handle those fields only known at runtime. I want the equivalent of
string fieldName = 'PhoneNumber = ';
string fieldSource = 'pd.tbList_DataText';
string expression = 'pd=>new { ID = pd.ID, PrimaryData=pd.PrimaryData';
expression += fieldName;
expression += FieldSource;
expression += '.Where(ld => ld.DataRowID == pd.ID && ld.ListColumnID == 1).Select(ld => ld.DataField).DefaultIfEmpty("").First()})';
var results = primaryData.Select(expression);
So how do I make that work? Thanks
// Get base Data
IEnumerable<tbList_Data> primaryData = await _tbList_DataRepository.GetAsync(ld => ld.ListID == listId && (ld.IsActive == includeInactive ? ld.IsActive : true));
// Get Final Results
var results = primaryData.Select(pd => new {
Id = pd.ID,
PrimaryData = pd.PrimaryData,
PhoneNumber = pd.tbList_DataText.Where(ld => ld.DataRowID == pd.ID && ld.ListColumnID == 1).Select(ld => ld.DataField).DefaultIfEmpty("").First()
});
I see several options.
1) Tuple
var results = primaryData.Select(pd => new {
Id = pd.ID,
PrimaryData = pd.PrimaryData,
Extra = Tuple.Create("PhoneNumber", pd.tbList_DataText.Where(ld => ld.DataRowID == pd.ID && ld.ListColumnID == 1).Select(ld => ld.DataField).DefaultIfEmpty("").First())
});
// How to access:
foreach (var result in results)
{
Console.WriteLine(result.Id);
Console.WriteLine(result.PrimaryData);
Console.WriteLine(result.Extra.Item1);
Console.WriteLine(result.Extra.Item2);
}
2) Dictionary
// Using C# 6 notation
var results = primaryData.Select(pd => new Dictionary<string, object>{
["Id"] = pd.ID,
["PrimaryData"] = pd.PrimaryData,
["PhoneNumber"] = pd.tbList_DataText.Where(ld => ld.DataRowID == pd.ID && ld.ListColumnID == 1).Select(ld => ld.DataField).DefaultIfEmpty("").First()
});
// Using C# 5 notation
var results = primaryData.Select(pd => new Dictionary<string, object>{
{"Id", pd.ID},
{"PrimaryData", pd.PrimaryData},
{"PhoneNumber", pd.tbList_DataText.Where(ld => ld.DataRowID == pd.ID && ld.ListColumnID == 1).Select(ld => ld.DataField).DefaultIfEmpty("").First()}
});
// How to access:
foreach(var result in results)
{
Console.WriteLine(result["Id"]);
Console.WriteLine(result["PrimaryData"]);
Console.WriteLine(result["PhoneNumber"]);
}
3) Dynamic
var results = primaryData.Select(pd => {
dynamic result = new System.Dynamic.ExpandoObject();
result.Id = pd.ID;
result.PrimaryData = pd.PrimaryData;
// Choose one of the following. Since you only "PhoneNumber" at runtime, probably the second one.
result.PhoneNumber = pd.tbList_DataText.Where(ld => ld.DataRowID == pd.ID && ld.ListColumnID == 1).Select(ld => ld.DataField).DefaultIfEmpty("").First();
((IDictionary<string, object>)result).Add("PhoneNumber", pd.tbList_DataText.Where(ld => ld.DataRowID == pd.ID && ld.ListColumnID == 1).Select(ld => ld.DataField).DefaultIfEmpty("").First());
return result;
});
// How to access:
foreach(var result in results)
{
Console.WriteLine(result.Id);
Console.WriteLine(result.PrimaryData);
// Both work, independently how you created them
Console.WriteLine(result.PhoneNumber);
Console.WriteLine(((IDictionary<string, object>)result)["PhoneNumber"]);
}
EDIT: just realized from question that the field source should be dynamic as well. So, in the above code, replace any occurrence of pb.tbList_DataText by:
((IEnumerable<X>)pb.GetType().GetField("tbList_DataText").GetValue(pb))
Where X should be the type of ld. But carefull! This cast can potentially fail.
Also, if you want a property instead of a field, just use GetProperty instead of GetField.

Build a dynamic where clause over multiple properties

What I have is a List<string> IndexFields which contains a list of property names.
My issue is that I need to build a where clause based on the elements in the list.
So far I have;
var sitem = List1.Where(p => (p.GetType().GetProperty(IndexFields[0])
.GetValue(p, null) as string) == "red").FirstOrDefault();
But that only allows me to specify a single property. What I need is a builder that can build based on all the names in the List<string> IndexFields list.
The most flexible way to create dynamic queries at runtime is by using the Expression API:
For example:-
var type = typeof(T);
var properties = IndexFields.Select(x => type.GetProperty(x));
// x
var paramter = Expression.Parameter(type);
// x.Foo, x.Bar, ...
var leftHandSides = properties.Select(
x => Expression.Property(parameter, x));
// "Baz"
var rightHandSide = Expression.Constant(...);
// x.Foo == "Baz", x.Bar = "Baz", ...
var equalityExpressions = leftHandSides.Select(
x => Expression.Equal(x, rightHandSide));
// x.Foo == "Baz" && x.Bar == "Baz" && ...
var aggregatedExpressions = equalityExpressions.Aggregate(
(x, y) => Expression.AndAlso(x, y));
// x => x.Foo == "Baz" && x.Bar == "Baz" && ...
var lambda = Expression.Lambda<Func<T,bool>>(
aggregatedExpressions, parameter)
var item = List1.Where(lambda).FirstOrDefault();
A huge advantage of building your queries like this is that the resulting expression can still be e.g. translated into SQL for use with Entity Framework, wheras using reflection inside the body of your lambda is really limiting.
I really do recommend taking some time to really understand the expression framework before using it, though. If you can grok what's going on, it saves you a ton of time in the long run.
You can read more at e.g:-
http://www.digitallycreated.net/Blog/37/dynamic-queries-in-entity-framework-using-expression-trees
https://stackoverflow.com/questions/1217539/net-expression-trees-tutorial
If you're looking for something quicker and dirtier, however, you can just go ahead and chain up those Where clauses inside a foreach:-
IEnumerable<T> query = List1;
foreach (var property in IndexFields)
{
// The variable "property" gets hoisted out of local context
// which messes you up if the query is being evaluated with
// delayed execution.
// If you're working in C# 6 though, you don't need to do this.
var localProperty = property;
query = query.Where(
p => (p.GetType().GetProperty(localProperty)
.GetValue(p, null) as string) == "red");
}
var sitem = query.FirstOrDefault();
You can use a PredicateBuilder for this:
var predicate = PredicateBuilder.New<string>();
if (aCondition)
{
predicate = predicate.And(s => s == "this");
}
if (bCondition)
{
predicate = predicate.And(s => s == "that");
}
you can try something like this, worked for me in linqpad
void Main() {
var listFields = new string[] { "Field1", "Field2" };
var listValues = new string[] { "value1", "value2" };
// prepare & show dummy data
var listItems = Enumerable.Range(1, 100).Select(aaIndex => new MyItem {
Name = string.Format("item{0}", aaIndex),
Field1 = string.Format("value{0}", aaIndex % 3),
Field2 = string.Format("value{0}", aaIndex % 7)
});
listItems.Dump();
// apply filtering
var filtered = listItems.Where(aaItem => Enumerable.Range(0, listFields.Length).All(aaIndex => {
var value1 = aaItem.GetType().GetProperty(listFields[aaIndex]).GetValue(aaItem, null);
var value2 = listValues[aaIndex];
if (value1 is IComparable) {
return ((IComparable)value1).CompareTo(value2) == 0;
}
return Convert.ToString(value1) == Convert.ToString(value2);
}));
filtered.Dump();
}
// Define other methods and classes here
class MyItem {
public string Name { get; set; }
public string Field1 { get; set; }
public string Field2 { get; set; }
}
The dynamic linq library worked well for stuff like this:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
It added overloads to take the different clauses as strings ie:
var query = List1.Where("Color1=""Red"" or Color2=""Red""");
In your case you could build the string from your index fields (probably in a loop but simplified here)
var query = List1.Where(IndexFields[0] + "=""Red"" or " IndexFields[1] + "=Red");
For use, download the sample package, and then grab LinqSamples\DynamicQuery\DynamicQuery\Dynamic.cs and compile with your project.

filter a porperty(List<Foo>) that belong to to object

Now i want to return a sequence that list of X that its prop1.where(p=>p.a==1).
i can write this with Select clause but my object has many properties.
something like this(but in true syntax):
ctx.MyObject.Where(p=>p.state==1 && prop1.where(p=>p.a==1));
EDIT: obj1 with this props(int a, List<Foo> prop1) and Foo has (int b,int c).
Depending what you want to do, try Any instead of Where:
ctx.MyObject.Where(p=>p.state==1 && prop1.Any(p2 => p2.a == 1))
Or as you mentioned you can use Select:
ctx.MyObject
.Where(p => p.state == 1)
.Select(p => new
{
state = p.state,
prop1 = p.prop1.Where(p2 => p2.a == 1),
// other fields...
}

Categories