How to refactor linq predicate into Func variable - c#

I have this code:
if (model.Id > 0)
{
return _buyerRepository.Exists(x => (model.Id != x.Id) &&
(x.IdentificationNumber == model.IdentificationNumber));
}
return _buyerRepository.Exists(x =>
x.IdentificationNumber == model.IdentificationNumber);
How to extract (model.Id != x.Id) and to put in the linq query based on if model.Id>0 or it's a 0 ?
I am thinking on ExpressionTrees and Func<T,bool>, but Im not sure how to put a condition.

I would move from Exists to Any and do the following:
IEnumerable<T> query = (IEnumerable<T>)_buyerRepository;
if (model.Id > 0)
query = query.Where(x => model.Id != x.Id);
return query.Any(x => x.IdentificationNumber == model.IdentificationNumber);
See an example here:
https://dotnetfiddle.net/EkoDym
public class Program
{
public static void Main()
{
var foos = new List<Foo>()
{
new Foo() { Id = 1, IdentificationNumber = "1" },
new Foo() { Id = 2, IdentificationNumber = "2" },
};
Console.WriteLine(HasAny(foos, 1, "1"));
Console.WriteLine(HasAny(foos, 0, "1"));
var foosv2 = new List<Foo>()
{
new Foo() { Id = 2, IdentificationNumber = "1" },
};
Console.WriteLine(HasAny(foosv2, 1, "1"));
Console.WriteLine(HasAny(foosv2, 2, "1"));
}
static bool HasAny(List<Foo> foos, int id, string identificationNumber)
{
IEnumerable<Foo> query = (IEnumerable<Foo>)foos;
if (id > 0)
query = query.Where(x => id != x.Id);
return query.Any(x => x.IdentificationNumber == identificationNumber);
}
}
public class Foo
{
public int Id { get; set; }
public string IdentificationNumber { get; set; }
}
In case you may believe that the Where condition will be evaluated for all items before Any you don't have to worry about it that is the laziness of LINQ and Where will only be evaluated till Any finds a result.
You can see here a demo:
https://dotnetfiddle.net/Wen09a
public class Program
{
public static void Main()
{
var foos = new List<Foo>()
{
new Foo() { Id = 1, IdentificationNumber = "1" },
new Foo() { Id = 2, IdentificationNumber = "2" },
new Foo() { Id = 3, IdentificationNumber = "3" },
new Foo() { Id = 4, IdentificationNumber = "4" },
new Foo() { Id = 5, IdentificationNumber = "5" },
new Foo() { Id = 6, IdentificationNumber = "6" },
};
Console.WriteLine(HasAny(foos, 1, "2"));
}
static bool HasAny(List<Foo> foos, int id, string identificationNumber)
{
IEnumerable<Foo> query = (IEnumerable<Foo>)foos;
if (id > 0)
query = query.Where(x =>
{
Console.WriteLine($"Compare Ids - {id} and {x.Id}");
return id != x.Id;
});
return query.Any(x => x.IdentificationNumber == identificationNumber);
}
}
public class Foo
{
public int Id { get; set; }
public string IdentificationNumber { get; set; }
}
Even though I have declared the Ids 3 to 6 which would all be unequal to the id 1 it will stop searching on the first positive value for Any.

Related

c# cross join two same type of lists

So here I have some code, which works ok. But I want to change the select part to something else, I am not sure what other methods I can use any help would be appreciated.
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var all = new List<People>{new People{Id = 1, Name = "andy1", Age = null}, new People{Id = 2, Name = "andy2", Age = null}, new People{Id = 3, Name = "andy3", Age = null}, new People{Id = 4, Name = "andy4", Age = null}, };
var someOfThem = new List<People>{new People{Id = 1, Name = null, Age = 1}, new People{Id = 2, Name = null, Age = 1},new People{Id = 3, Name = null, Age = 1}};
var test = someOfThem.Select(c =>
{
c.Name = all.Find(a => a.Id == c.Id).Name;
return c;
});
foreach (var item in test)
Console.WriteLine("{0}={1}={2}", item.Id, item.Name, item.Age);
}
}
public class People
{
public int Id
{
get;
set;
}
public int? Age
{
get;
set;
}
public string Name
{
get;
set;
}
}
And here is the result.
1=andy1=1
2=andy2=1
3=andy3=1
I am just wondering is there another way to achieve the same result but a more elegant way? or an easier way?
var test = someOfThem.Select(c =>
{
c.Name = all.Find(a => a.Id == c.Id).Name;
return c;
});
Update
Sorry I did not show my problem properly at first, I have updated my quesiton. Please have a look again.
You can use C#'s LINQ keywords and more specifically, the join keyword assosciated with it:
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var all = new List<People> { new People { Id = 1, Name = "andy1", }, new People { Id = 2, Name = "andy2", }, new People { Id = 3, Name = "andy3", }, new People { Id = 4, Name = "andy4", }, };
var someOfThem = new List<People> { new People { Id = 1, Name = null, }, new People { Id = 2, Name = null, } };
var test = from item in someOfThem
join element in all on item.Id equals element.Id
select element;
foreach (var item in test)
Console.WriteLine("{0}={1}", item.Id, item.Name);
}
}
public class People
{
public int Id
{
get;
set;
}
public string Name
{
get;
set;
}
}
The code version would be
var test = someOfThem.Join(all, item => item.Id, element => element.Id, (item, element) => element);
as shown in Robert's comment
You can use the Join (you can also use a dictionary, but I'm not going to show it):
Here's the syntax for join:
var test = someOfThem.Join(all, item => item.Id, element => element.Id,
(item, element) => new Person {
Id = item.Id ?? element.Id,
Name = item.Name ?? element.Name,
Age = item.Age ?? element.Age
});
You can implement Equals and GetHashCode in your People class and use Intersect.
Or, create an EqualityComparer, that way your comparison logic is decoupled:
class Program
{
public static void Main()
{
var all = new List<People> { new People { Id = 1, Name = "andy1", }, new People { Id = 2, Name = "andy2", }, new People { Id = 3, Name = "andy3", }, new People { Id = 4, Name = "andy4", }, };
var someOfThem = new List<People> { new People { Id = 1, Name = null, }, new People { Id = 2, Name = null, } };
var test = all.Intersect(someOfThem, new PeopleIdComparer()).ToList();
foreach (var item in test)
Console.WriteLine("{0}={1}", item.Id, item.Name);
}
}
public class PeopleIdComparer : IEqualityComparer<People>
{
public bool Equals(People x, People y)
{
return x.Id == y.Id;
}
public int GetHashCode(People obj)
{
return HashCode.Combine(obj.Id);
}
}
public class People
{
public int Id
{
get;
set;
}
public string Name
{
get;
set;
}
}

How to merge two different List

I have a List<string> list1, example values:
var list1 = new List<string>()
{
"123", "1234", "12345",
};
I have a class:
public class TestClass {
public string name{ get; set; }
public int count{ get; set; }
}
and I have a List<TestClass> list2, example values:
var list2 = new List<TestClass>()
{
new TestClass() { name = "12", count = 0 },
new TestClass() { name = "123", count = 5 },
new TestClass() { name = "1234", count = 20 },
};
I want to merge list1 and list2 and the result should be:
name count
12 0
123 5
1234 20
12345 0
This works nicely:
var list1 = new List<string>()
{
"123", "1234", "12345",
};
var list2 = new List<TestClass>()
{
new TestClass() { name = "12", count = 0 },
new TestClass() { name = "123", count = 5 },
new TestClass() { name = "1234", count = 20 },
};
var merged =
list2
.Concat(list1.Select(x => new TestClass() { name = x, count = 0 }))
.GroupBy(x => x.name)
.SelectMany(x => x.Take(1))
.ToList();
It gives me:
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
List<TestClass> lst1 = new List<TestClass>();
lst1.Add(new TestClass(){name="One", count = 1});
lst1.Add(new TestClass(){name="Two", count = 2});
lst1.Add(new TestClass(){name="Three", count = 3});
List<TestClass> lst2 = new List<TestClass>();
lst2.Add(new TestClass(){name="Four", count = 4});
lst2.Add(new TestClass(){name="Two", count = 2});
lst2.Add(new TestClass(){name="Three", count = 3});
var unionlst = lst1.Union(lst2, new TestClassComparer ());
foreach(var x in unionlst){
Console.WriteLine(x.name + ","+x.count);
}
}
class TestClassComparer : IEqualityComparer<TestClass>
{
public bool Equals(TestClass p1, TestClass p2)
{
return p1.name == p2.name && p1.count == p2.count;
}
public int GetHashCode(TestClass p)
{
return p.count;
}
}
public class TestClass {
public string name{ get; set; }
public int count{ get; set; }
}
}
Sample output:
One,1
Two,2
Three,3
Four,4

Set PredicateBuilder also on child collection

I am trying to apply the predicate not only to the entity parent but also to the child collection. The following is part of my code:
var predicate = PredicateBuilder.New<Entity>();
foreach (string keyword in keywords)
{
string temp = keyword;
predicate = predicate.Or(p => p.Name.Equals(temp));
predicate = predicate.Or(p => p.Addresses.Select(x=> x.Name.Equals(temp));
}
The line predicate = predicate.Or(p => p.Addresses.Select(x=> x.Name.Equals(temp)); is not working ? any ideas as to why?
EDIT
In the following demo I am asking to get entity parent and child with name = tom but I also get child name = tom2.
private static void Main(string[] args)
{
var result = GetAll(GetAll(), new List<string> { "tom" });
}
private static List<User> GetAll()
{
return new List<User>
{
new User
{
Id = 1,
Name = "tom",
Addesses = new List<Addesses>
{
new Addesses { Id = 1, Name = "tom" },
new Addesses { Id = 1, Name = "tom2" },
}
},
new User
{
Id = 1,
Name = "sam",
Addesses = new List<Addesses>
{
new Addesses { Id = 1, Name = "sam" },
new Addesses { Id = 1, Name = "sam2" },
}
},
};
}
private static List<User> GetAll(List<User> users, List<string> keywords)
{
var predicate = PredicateBuilder.New<User>();
foreach (string keyword in keywords)
{
string temp = keyword;
predicate = predicate.Or(p => p.Name.Equals(temp));
predicate = predicate.Or(p => p.Addesses.Any(x => x.Name.Equals(temp)));
}
var result = users
.Where(predicate)
.ToList();
return result;
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public List<Addesses> Addesses { get; set; }
}
public class Addesses
{
public int Id { get; set; }
public string Name { get; set; }
}
The call to p.Addresses.Select(x=> x.Name.Equals(temp) is not returning a boolean result.
Depending on your actual logic you may want to look into Any:
predicate = predicate.Or(p => p.Addresses.Any(x=> x.Name.Equals(temp));
or All:
predicate = predicate.Or(p => p.Addresses.All(x=> x.Name.Equals(temp));

Code review of this portion of Linq Filters

I am a newbie of c #, I would like to know if I can remove the for each and do a single operation with Linq. I would like to return an IEnumerable with already filtered. is it possible to do this? Every suggestion is welcome, thank you very much
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Linq
{
class Oggetto
{
public int Id { get; set; }
public string MyProperty { get; set; }
public int Deleted { get; set; }
}
class Program
{
static void Main(string[] args)
{
IEnumerable<Oggetto> lista = new List<Oggetto> {
new Oggetto(){ Id = 1, MyProperty = "Propr1", Deleted = 0 },
new Oggetto(){ Id = 1, MyProperty = "Propr2", Deleted = 1 },
new Oggetto(){ Id = 2, MyProperty = "Prop3", Deleted = 0 },
new Oggetto(){ Id = 3, MyProperty = "Propr4", Deleted = 0 },
new Oggetto(){ Id = 3, MyProperty = "Prop5", Deleted = 1 }
};
foreach (var item in lista.Where(x => x.Deleted == 1).GroupBy(x => x.Id).Select(g => g.First()))
{
item.MyProperty = string.Join(",", lista.Where(t => t.Id == item.Id).Select(x => x.MyProperty).ToArray());
Console.WriteLine(item.Id);
Console.WriteLine(item.MyProperty);
}
Console.ReadLine();
}
}
}
You can use projection for this.
var orderedList = lista.GroupBy(x => x.Id)
.Where(x => x.Any(y => y.Deleted == 1))
.Select(x => new Oggetto
{
Id = x.Key, MyProperty = string.Join(",", x.Select(v => v.MyProperty))
});
foreach (var item in orderedList)
{
Console.WriteLine(item.Id);
Console.WriteLine(item.MyProperty);
}
Anyway, as #Alex said you shoud replace Deleted field type to bool and as said by #Marco Salerno start programming in English you'll not regret.
First of all I would avoid the groupBy statement. This is a lot of unneded overhead. You can use distinct instead. This will give you all the IDs you need to know.
var ids = lista.Where(x => x.Deleted).Select(x => x.Id).Distinct();
You can then select all the elements that you need with:
var items = ids.Select(i => lista.Where(x => x.Id == i));
which results in a List of Lists. For the ease of use I would convert this to a Dictionary<K, V> (int this case it's Dictionary<long, List<string>> as a final step:
var dictionary = items.ToDictionary(l => l.First().Id, l => l.Select(o => o.MyProperty).ToList());
You now got a "nice and filtered" collection you can use any way you like (or just output it)
foreach (var item in dictionary)
{
Console.WriteLine($"Id: {item.Key}");
Console.WriteLine($"Properties: {string.Join(", ", item.Value)}");
}
I also changed your class a little bit to:
class Oggetto
{
public int Id { get; set; }
public string MyProperty { get; set; }
// bool instead of int - Deleted has only 2 states
public bool Deleted { get; set; }
}
First of all STOP programming in Italian, start doing it in English.
Anyway, this should be a better approach:
class Program
{
static void Main(string[] args)
{
List<Item> items = new List<Item> {
new Item{ Id = 1, MyProperty = "Propr1", Deleted = 0 },
new Item{ Id = 1, MyProperty = "Propr2", Deleted = 1 },
new Item{ Id = 2, MyProperty = "Prop3", Deleted = 0 },
new Item{ Id = 3, MyProperty = "Propr4", Deleted = 0 },
new Item{ Id = 3, MyProperty = "Prop5", Deleted = 1}
};
foreach (IGrouping<int,Item> group in items.GroupBy(x => x.Id).ToList())
{
List<Item> groupItems = group.ToList();
Item deletedItem = groupItems.Where(x => x.Deleted == 1).FirstOrDefault();
if(deletedItem != null)
{
deletedItem.MyProperty = string.Join(",", groupItems.Select(x => x.MyProperty).ToArray());
Console.WriteLine(deletedItem.Id);
Console.WriteLine(deletedItem.MyProperty);
}
}
}
}
class Item
{
public int Id { get; set; }
public string MyProperty { get; set; }
public int Deleted { get; set; }
}

How can I sort a list that has potential parents with the parent objects first in the list?

In C#, if I have a list of objects, where each object can have a parent, and each parent can have a parent (x combinations), how is the best way to sort the list so that all the parent objects are first in the list?
Here is the object structure:
class test
{
int id { get; set; }
int parentId { get; set; }
bool hasParentObject { get; set; }
}
Here is an example of some objects:
Object a:
id = 1;
parentId = 0;
hasParentObject = false;
Object b:
id = 2;
parentId = 1;
hasParentObject = true;
Object c:
id = 3;
parentId = 2;
hasParentObject = true;
Thanks.
EDIT
With the following code, how can the code be modified so that if an object does not have a parent, the object is in the list before any objects that do have parents?
Code:
class Test : IComparable<Test>
{
public int Id { get; set; }
public int ParentId { get; set; }
public bool HasParentObject { get; set; }
public int CompareTo(Test other)
{
if(!this.HasParentObject)
return 1;
else if(!other.HasParentObject)
return 1;
else if(other.HasParentObject && this.HasParentObject)
return ParentId.CompareTo(other.ParentId);
else if(other.HasParentObject)
return 1;
else
return -1;
}
}
It sounds like you would want the objects that are parents first in the list (Optional: Ordered by ID), then you want the objects that aren't parents to follow the parent objects in the list (Optional: Ordered by ID).
This isn't a one liner, but I think it does what you're asking
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
List<Test> tests = new List<Test>
{
new Test() { Id = 6, ParentId = 2, HasParentObject = true },
new Test() { Id = 2, ParentId = 0, HasParentObject = false },
new Test() { Id = 1, ParentId = 0, HasParentObject = true },
new Test() { Id = 4, ParentId = 1, HasParentObject = true }
};
// Get the parents sorted
List<Test> sortedTests = tests.Where(t => tests.FindIndex(t2 => t2.ParentId == t.Id) != -1)
.OrderBy(t => t.Id)
.ToList();
// Add those that aren't parents sorted
sortedTests.AddRange(tests.Where(t => tests.FindIndex(t2 => t2.ParentId == t.Id) == -1)
.OrderBy(t => t.Id));
sortedTests.ForEach(t => Console.WriteLine("ID: {0} ParentId: {1}", t.Id, t.ParentId));
}
}
class Test
{
public int Id { get; set; }
public int ParentId { get; set; }
public bool HasParentObject { get; set; }
}
Results:
ID: 1 ParentId: 0
ID: 2 ParentId: 0
ID: 4 ParentId: 1
ID: 6 ParentId: 2
Fiddle Demo
You could implement the IComparable itherface for your class and then use the Sort method of the list.
class Test : IComparable<Test>
{
public int Id { get; set; }
public int ParentId { get; set; }
public bool HasParentObject { get; set; }
public int CompareTo(Test other)
{
if(other.HasParentObject && this.HasParentObject)
return ParentId.CompareTo(other.ParentId);
else if(other.HasParentObject)
return 1;
else
return -1;
}
}
Please look here for a working example based on your post's data.
if one can assume the hasParentObject property is public
public List<test> l;
public List<test> sortList(List<test> _l)
{
return _l.OrderByDescending(a => a.hasParentObject).ToList();
}
You can use new Comparison overload of Sort method - .Sort(new Comparison<test>(expression))
For instance :
List<test> list = new List<test>()
{
new test() { id=1, hasParentObject = false, parentId = 0 },
new test() { id=2, hasParentObject = true, parentId = 1 },
new test() { id=3, hasParentObject = false, parentId = 0 },
new test() { id=4, hasParentObject = true, parentId = 3 },
};
list.Sort((s1, s2) => s1.parentId > s2.parentId ? 1 : (s1.parentId < s2.parentId ? -1 : 0));
foreach (var item in list)
Console.WriteLine(item.id);
Console.ReadKey();

Categories

Resources