i think this question has been asked before but i havent been able to deduce a clear answer. I am trying to find the best way (or a way) to intersect two completely different ienumerable collections.
class A:
int z1
int z2
int z3
string z4
class B:
int j5
int j6
T j7
T j8
string j9
..I want to intersect List<A> with List<B> on z2 == j6.
can this be done?
The question doesn't really make sense - what would the result type be? Intersections have to be performed on two sequences of the same type. It sounds like you don't so much want an intersection between two sets, as a filter of the first sequence based on possible values of z2. For example:
HashSet<int> validZ2 = new HashSet<int>(listB.Select(x => x.j6));
var filtered = listA.Where(x => validZ2.Contains(x.z2));
Or possibly as Gabe suggests, you want a join. For example:
var query = from a in listA
join b in listB on a.z2 equals b.j6
select new { a, b };
That will give you all pairings of values from the two lists which match on z2/j6.
You need to implement a custom equality comparer (see IEqualityComparer<T> interface) to pass it as a second argument to Intersect().
By using the intersect method, you can get common members between the two enumerables, like this example demonstrates:
[Test]
public void TestObjectIntersect()
{
var a = new List<object> { 1, 2, 3, "test", "test2" };
var b = new List<object> { 4, 5, 1, "test2" };
var c = a.Intersect(b);
Console.WriteLine(String.Join(",", c.Select(x => x.ToString()).ToArray()));
}
class Program
{
static void Main(string[] args)
{
var aList = (from item in Enumerable.Range(1, 10)
select new A { Z1 = item, Z2 = item * 2 }).ToList();
var bList = (from item in Enumerable.Range(10, 100)
select new B { J5 = item, J6 = item / 2 }).ToList();
var intersect = (from a in aList
join b in bList
on a.Z2 equals b.J6
select new { A = a, B = b }).ToList();
foreach (var item in intersect)
{
Console.WriteLine("A:{{{0}}}, B:{{{1}}}", item.A, item.B);
}
}
}
public class A
{
public int Z1 { get; set; }
public int Z2 { get; set; }
// other fields and properties
public override string ToString()
{
return string.Format("Z1={0}, Z2={1}", Z1, Z2);
}
}
public class B
{
public int J5 { get; set; }
public int J6 { get; set; }
// other fields and properties
public override string ToString()
{
return string.Format("J5={0}, J6={1}", J5, J6);
}
}
Related
I have two lists, one list type is int and the other type is string.
List<int> IntList = new List<int>(){1,2,3,4,5};
List<string> StringList = new List<string>(){"a","b","c","d","e"};
I want to combine these two lists to a new list.
And I create a new class called Table
public class Table
{
public int a { get; set; }
public string b { get; set; }
}
How can i combine IntList and StringList to list<Table>
Like this output
List<Table>
{a=1,b=a}
{a=2,b=b}
{a=3,b=c}
{a=4,b=d}
{a=5,b=e}
Edite
if i want to add the third list
list<int>{0,0,0,0,0}
public class Table
{
public int a { get; set; }
public string b { get; set; }
public string c { get; set; }
}
output
List<Table>
{a=1,b=a,c=0}
{a=2,b=b,c=0}
{a=3,b=c,c=0}
{a=4,b=d,c=0}
{a=5,b=e,c=0}
Edite again
Under the URL of this funtion is vey useful ,it can combine multiple list in to one list
Title :Merge multiple Lists into one List with LINQ
Merge multiple Lists into one List with LINQ
You can use the System.Linq extension method Zip() to return a Table object for each pair of corresponding elements from the two sequences:
List<Table> result = IntList?.Zip(StringList, (i, s) => new Table {a = i, b = s}).ToList();
It looks like you want to use the List Index to join them,
so you can use LINQ Join and List.IndexOf to do it.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
List<int> IntList = new List<int>() { 1, 2, 3, 4, 5 };
List<string> StringList = new List<string>() { "a", "b", "c", "d", "e" };
var expected = (from t1 in IntList
join t2 in StringList on IntList.IndexOf(t1) equals StringList.IndexOf(t2)
select new Table { a = t1, b = t2 }
).ToList();
Console.WriteLine(expected);
}
}
public class Table
{
public int a { get; set; }
public string b { get; set; }
}
Online Demo Link | C# Online Compiler | .NET Fiddle
The Linq Zip method can do this.
IntList.Zip(StringList, (i, s) => new Table { a = i, b = s });
You can do it with Linq Select()
//If IntList and StringList are of same size
var result = IntList.Select((x, i) => new Table(){a = x, b = StringList[i]});
If length is not equal, then you can add if..else loop,
List<Table> result = new List<Table>();
if(IntList.Count() > StringList.Count())
result = StringList.Select((x, i) => new Table(){a = IntList[i], b = x}).ToList();
else
result = IntList.Select((x, i) => new Table(){a = x, b = StringList[i]}).ToList();
Online Demo: .Net Fiddle
I have a collection of objects to be ordered by an object's field value. Current problem is that the order depends on a business logic.
public enum Order : byte {
a = 1,
b = 2,
c = 3
}
public class Foo{
public long A {get;set;}
public long B {get;set;}
public long C {get;set;}
}
public class Worker(){
private Foo[] orderFoos(Foo[] foos, Func<Order, long> sort){
return foos.OrderByDescending(f => sort(f)).ToArray(foos.Length);
}
public void Work(){
Foo[] foos = getFoos();
var orderByA = orderFoos(foos, f => f.A);
var orderByB = orderFoos(foos, f => f.B);
var orderByC = orderFoos(foos, f => f.C);
}
}
Compiler throws an error that Argument 1: cannot convert from 'Foo' to 'Order'. Are there any workarounds or solutions?
It seems what you wanted to achieve is sorting on different fields. You may not need to have Order enum if it's only for that purpose and replace:
private Foo[] orderFoos(Foo[] foos, Func<Order, long> sort){
return foos.OrderByDescending(f => sort(f)).ToArray(foos.Length);
}
into
private Foo[] orderFoos(Foo[] foos, Func<Foo, long> sort){
return foos.OrderByDescending(sort).ToArray(foos.Length);
}
NB: I'm not sure your intention with adding foos.Length in the ToArray method, but supposedly that's out of the scope of the question.
The below code seems to work. It is a small change to the orderFoos method, with some sample code for you to test the results.
using System;
using System.Linq;
public enum Order : byte
{
a = 1,
b = 2,
c = 3
}
public class Foo
{
public long A { get; set; }
public long B { get; set; }
public long C { get; set; }
}
public class Worker
{
private Foo[] orderFoos(Foo[] foos, Func<Foo, long> sort)
{
return foos.OrderByDescending(sort).ToArray();
}
public void Work()
{
Foo[] foos = { new Foo() { A = 1, B = 2, C = 3 }, new Foo() { A = 10, B = 1, C = 2 }, new Foo() { A = -1, B = 1, C = 10 } };
var orderByA = orderFoos(foos, f => f.A);
var orderByB = orderFoos(foos, f => f.B);
var orderByC = orderFoos(foos, f => f.C);
Console.WriteLine(orderByA.First().A); // I expect the second to be first here so 10
Console.WriteLine(orderByB.First().A); // I expect the first to be first here so 1
Console.WriteLine(orderByC.First().A); // I expect the third to be first here so -1
}
}
class Program
{
static void Main(string[] args)
{
var worker = new Worker();
worker.Work();
Console.ReadLine();
}
}
#hsoesanto gave a good solution but it doesn't work the way I expected it would be.
So I've created temporary workaround.
private Func<Foo, long> GetOrderFunction(Order orderType)
{
switch (orderType)
{
case Order.A:
return (f) => f.A;
case Order.B:
return (f) => f.B;
case Order.C:
return (f) => f.C;
}
}
private Foo[] orderFoos(Foo[] foos, Order order)
{
var orderFunction = GetOrderFunction(order);
return foos
.OrderByDescending(f => orderFunction (f))
.ToArray(foos.Length);
}
I have two list of objects. For e.g. say the objects are like below
class A
{
int ID;
int Value;
}
class B
{
int ID;
int Value;
}
I have two list of above objects like List<A> AList and List<B> BList. I want to find if any object in List<B> has matching Value from List<A>.
For now, what I do like is
foreach(var item in AList)
{
if(!BList.Any(x => x.Value == item.Value))
{
//Handle the error message
}
}
Is there any better way to do it by Linq?
You can do it this way. This will be true if there are any items in BList that have matching values in AList:
BList.Any(b => AList.Select(a => a.Value).Contains(b.Value))
Simply:
from a in AList
join b in BList on a.Value equals b.Value
select a
Try this:
BList.Any(b => AList.Any(a => a.Value == b.Value));
According to your current code, and if you just need to handle error when any item in AList doesn't have a matching item in BList, you can do as follows :
if (AList.Any(a => !BList.Any(b => b.Value == a.Value)))
{
//Handle error
}
Or if you need to take an action on every item in AList that doesn't have a matching item in BList :
foreach(var item in AList.Where(a => !BList.Any(b => b.Value == a.Value)))
{
//Handle error for current `item`
}
Anyways, the reason to prefer LINQ over conventional foreach loop is usually more for its readability (shorter, cleaner, easier to maintain, etc.) rather than performance. For reference : Is a LINQ statement faster than a 'foreach' loop?
This is what I've tried and it seems to work just fine:
class First
{
public int Id { get; set; }
public int Value { get; set; }
}
class Second
{
public int Id { get; set; }
public int Value { get; set; }
}
class Program
{
static void Main(string[] args)
{
var firstList = new List<First>
{
new First { Id = 1, Value = 2 },
new First { Id = 1, Value = 10 },
new First { Id = 1, Value = 0 }
};
var secondList = new List<Second>
{
new Second { Id = 1, Value = 2 },
new Second { Id = 1, Value = 2 },
new Second { Id = 1, Value = 4 }
};
bool hasCommonValues = firstList.Select(f => f)
.Any(u => secondList.Select(x => x.Value)
.Contains(u.Value));
Console.WriteLine(hasCommonValues);
}
}
I have data I receive from a web service via HTTPWebRequest. After I parse it using NewtonSoft.Deserialize into a custom type (a simple class with public string properties), I want to manipulate this data using LINQ - more specifically, I want to group the data.
My problem is that the grouping works fine if I group by a single string property
from x in myList
group x by x.myStr into grp
select grp;
Since I want to group by more columns, I am returning a custom type with
new MyType { a = ..., b = ... }
The group is however not working. I thought the reason must be the compiler does not know how to compare these objects - so if this type implements IEqualityComparer<MyType> it will solve it.
But no, it is still not grouping accordingly, and it creates several keys with the exact same string values.
The custom type by which I am grouping is something like
public class MyType
{
public string a;
public string b;
public string c;
}
Any ideas of what am I missing?
Here's a concrete example of the scenario described above:
//The type that models the data returned from the web service
public class MyClass
{
public string a { get; set; }
public string b { get; set; }
public string c { get; set; }
public DateTime d { get; set; }
public DateTime e { get; set; }
}
// the type by which I want to group my data
public class MyGroup : IEquatable<MyGroup>, IEqualityComparer<MyGroup>
{
public string f1 { get; set; }
public DateTime d1 { get; set; }
public DateTime d2 { get; set; }
public bool Equals(MyGroup other)
{
return string.Compare(this.f1, other.f1) == 0;
}
public bool Equals(MyGroup x, MyGroup y)
{
return string.Compare(x.f1, y.f1) == 0;
}
public int GetHashCode(MyGroup obj)
{
return obj.GetHashCode();
}
}
List<MyClass> l = new List<MyClass>();
l.Add(new MyClass { a = "aaa", b = "bbb", c = "ccc", d = DateTime.ParseExact("20081405", "yyyyddMM", Thread.CurrentThread.CurrentCulture), e = DateTime.ParseExact("20140101", "yyyyddMM", Thread.CurrentThread.CurrentCulture) });
l.Add(new MyClass { a = "aaaa", b = "bbb", c = "ccc", d = DateTime.ParseExact("20090105", "yyyyddMM", Thread.CurrentThread.CurrentCulture), e = DateTime.ParseExact("20140201", "yyyyddMM", Thread.CurrentThread.CurrentCulture) });
l.Add(new MyClass { a = "aa", b = "bbbb", c = "cccc", d = DateTime.ParseExact("20081405", "yyyyddMM", Thread.CurrentThread.CurrentCulture), e = DateTime.ParseExact("20140201", "yyyyddMM", Thread.CurrentThread.CurrentCulture) });
l.Add(new MyClass { a = "aaa", b = "bbbbb", c = "ccc", d = DateTime.ParseExact("20121111", "yyyyddMM", Thread.CurrentThread.CurrentCulture), e = DateTime.ParseExact("20140101", "yyyyddMM", Thread.CurrentThread.CurrentCulture) });
l.Add(new MyClass { a = "aaaaa", b = "bbb", c = "ccc", d = DateTime.ParseExact("20081405", "yyyyddMM", Thread.CurrentThread.CurrentCulture), e = DateTime.ParseExact("20140101", "yyyyddMM", Thread.CurrentThread.CurrentCulture) });
l.Add(new MyClass { a = "aaaa", b = "bbbbb", c = "ccc", d = DateTime.ParseExact("20121111", "yyyyddMM", Thread.CurrentThread.CurrentCulture), e = DateTime.ParseExact("20140101", "yyyyddMM", Thread.CurrentThread.CurrentCulture) });
l.Add(new MyClass { a = "aaaa", b = "bbbb", c = "cccccc", d = DateTime.ParseExact("20081405", "yyyyddMM", Thread.CurrentThread.CurrentCulture), e = DateTime.ParseExact("20140201", "yyyyddMM", Thread.CurrentThread.CurrentCulture) });
l.Add(new MyClass { a = "aaaaa", b = "bbb", c = "cccc", d = DateTime.ParseExact("20090105", "yyyyddMM", Thread.CurrentThread.CurrentCulture), e = DateTime.ParseExact("20140301", "yyyyddMM", Thread.CurrentThread.CurrentCulture) });
l.Add(new MyClass { a = "aaa", b = "bbb", c = "cccc", d = DateTime.ParseExact("20081405", "yyyyddMM", Thread.CurrentThread.CurrentCulture), e = DateTime.ParseExact("20140201", "yyyyddMM", Thread.CurrentThread.CurrentCulture) });
//The following does not really group
//IEnumerable<IGrouping<MyGroup, MyClass>> r = from x in l
IEnumerable<IGrouping<string, MyClass>> r = from x in l
//group x by new MyGroup { f1 = x.a /*, d1 = x.d, d2 = x.e*/ } into grp
orderby x.a
group x by x.a into grp
select grp;
//foreach (IGrouping<MyGroup, MyClass> g in r)
foreach (IGrouping<string, MyClass> g in r)
{
//Console.WriteLine(g.Key.f1);
Console.WriteLine(g.Key);
}
I thought the reason must be the compiler does not know how to compare these objects - so if this type implements IEqualityComparer<MyType> it will solve it.
Actually, to use a custom "equality" check in Linq functions you need to implement IEquatable<T>. IEquatable<T> is used to compare an instance of an object with another object of the same type - while IEqualityProvider<T> is meant to be implemented by an external class to compare two arbitrary Ts (and/or to have multiple methods of determining "equality").
Note that you should also implement Object.Equals and Object.GetHashCode - IEquatable<T> just allows you to compare in a type-safe manner.
Why the need for overriding Object's Equals and GetHashCode?
To ensure that any method (Object.Equals(object), the static Object.Equals(object, object, etc.) used to compare two objects is consistent. And any time you override Equals, you should also override GetHashCode to ensure that objects can be properly stored in a hash-based collection like a Dictionary or HashSet.
What does it mean IEquitable only compares in a type-safe manner?
When using IEquatable<T>, the object you're comparing to is guaranteed to be a T (or a subtype of T), whereas with Object.Equals, you don't know the type of the other object and must check it's type first.
For example:
// IEquatable<T>.Equals()
public bool Equals(MyGroup other)
{
return string.Compare(this.f1, other.f1) == 0;
}
versus
// Object.Equals()
public bool Equals(object other)
{
// need to check the type of the passed in object
MyGroup grp = other as MyGroup;
// other is not a MyGroup
if(grp == null return false);
return string.Compare(this.f1, grp.f1) == 0;
// you could also use
// return this.Equals(grp);
// as a shortcut to reuse the same "equality" logic
}
Any ideas of what am I missing?
Something like:
public class MyType : IEquatable<MyType>
{
public string a;
public string b;
public string c;
public bool Equals(MyType other)
{
if (other == null)
return false;
if (GetType() != other.GetType()) // can be omitted if you mark the CLASS as sealed
return false;
return a == other.a && b == other.b && c == other.c;
}
public override bool Equals(object obj)
{
return Equals(obj as MyType);
}
public override int GetHashCode()
{
int hash = 0;
if (a != null)
hash ^= a.GetHashCode();
if (b != null)
hash ^= b.GetHashCode();
if (c != null)
hash ^= c.GetHashCode();
return hash;
}
}
Addition: Note that MyType above is mutable, and the hash code changes if one of the fields a, b and c are re-assigned. That is problematic if the re-assignment happens while the instance is being held in a Dictionary<MyType, whatever>, HashSet<MyType> etc.
Alternatively, you could "group by" an anonymous type as suggested in DavidG's answer, or "group by" Tuple.Create(.. , .. , ..).
Do your grouping using an anonymous type. The individual values themselves appear to be strings (i.e. simple types that already have built in comparers) so you don't need a custom type for anything other than the return. this way there is no need to worry about IEqualityComparer.
from x in myList
group x by new { x.myStr, x.otherStr, x.AnotherStr } into grp
select new MyTpe
{
a = grp.Key.myStr,
b = grp.Key.otherStr,
c = grp.Key.AnotherStr
};
Assuming
public class MyClass
{
public int ID {get; set; }
public string Name {get; set; }
}
and
List<MyClass> classList = //populate with MyClass instances of various IDs
I can do
List<MyClass> result = classList.FindAll(class => class.ID == 123);
and that will give me a list of just classes with ID = 123. Works great, looks elegant.
Now, if I had
List<List<MyClass>> listOfClassLists = //populate with Lists of MyClass instances
How do I get a filtered list where the lists themselves are filtered. I tried
List<List<MyClass>> result = listOfClassLists.FindAll
(list => list.FindAll(class => class.ID == 123).Count > 0);
it looks elegant, but doesn't work. It only includes Lists of classes where at least one class has an ID of 123, but it includes ALL MyClass instances in that list, not just the ones that match.
I ended up having to do
List<List<MyClass>> result = Results(listOfClassLists, 123);
private List<List<MyClass>> Results(List<List<MyClass>> myListOfLists, int id)
{
List<List<MyClass>> results = new List<List<MyClass>>();
foreach (List<MyClass> myClassList in myListOfLists)
{
List<MyClass> subList = myClassList.FindAll(myClass => myClass.ID == id);
if (subList.Count > 0)
results.Add(subList);
}
return results;
}
which gets the job done, but isn't that elegant. Just looking for better ways to do a FindAll on a List of Lists.
Ken
listOfClasses.SelectMany(x=>x).FindAll( /* yadda */)
Sorry about that, FindAll is a method of List<T>.
This
var result = from x in listOfClasses from y in x where SomeCondition(y) select y;
or
var result = listOfClasses.SelectMany(x=>x).Where(x=>SomeCondition(x));
To keep a list of lists, you could do something like this example:
MyClass a = new MyClass() { ID = 123, Name = "Apple" };
MyClass b = new MyClass() { ID = 456, Name = "Banana" };
MyClass c = new MyClass() { ID = 789, Name = "Cherry" };
MyClass d = new MyClass() { ID = 123, Name = "Alpha" };
MyClass e = new MyClass() { ID = 456, Name = "Bravo" };
List<List<MyClass>> lists = new List<List<MyClass>>()
{
new List<MyClass>() { a, b, c },
new List<MyClass>() { d, e },
new List<MyClass>() { b, c, e}
};
var query = lists
.Select(list => list.Where(item => item.ID == 123).ToList())
.Where(list => list.Count > 0).ToList();
query would be List<List<MyClass>> holding lists of MyClass objects that passed the test. At first glance, it looks out of order with the Where extension coming after the Select, but the transformation of the inner lists needs to occur first, and that's what's happening in the Select extension. Then it is filtered by the Where.
I would probably go with this
List<List<string>> stuff = new List<List<string>>();
List<List<string>> results = new List<List<string>>();
stuff.ForEach(list=> {var result = list.FindAll(i => i == "fun").ToList();
if (result.Count > 0) results.Add(result);
});
List<string> flatResult = new List<string>();
stuff.ForEach(List => flatResult.AddRange(List.FindAll(i => i == "fun")));
That way you can go with a jagged array or flatten it out.. But the Linq way works well too :-).
While producing a flat List<MyClass> will answer your need most of the time, the exact answer to your question is:
var result = (from list in ListOfClassLists
let listWithTheId=
(
(from myClass in list
where myClass.ID == id
select myClass)
.ToList()
)
where listWithTheId.Count > 0
select listWithTheId
).ToList();
This code snippet was taken from my Proof of Concept:
using System.Collections.Generic;
using System.Linq;
namespace ListOfListSelectionSpike
{
public class ListSpikeClass
{
public List<List<MyClass>> ListOfClassLists { get; set; }
private List<MyClass> list1, list2, list3;
public ListSpikeClass()
{
var myClassWithId123 = new MyClass("123");
var myClassWithIs345 = new MyClass("456");
list1 = new List<MyClass> { myClassWithId123, myClassWithIs345 };
list2 = new List<MyClass> { myClassWithId123, myClassWithIs345, myClassWithId123 };
list3 = new List<MyClass> { myClassWithIs345, myClassWithIs345 };
ListOfClassLists = new List<List<MyClass>> { list1, list2, list3 };
}
public List<List<MyClass>> GetListOfListsById(string id)
{
var result = (from list in ListOfClassLists
let listWithTheId =
((from myClass in list
where myClass.ID == id
select myClass)
.ToList())
where listWithTheId.Count > 0
select listWithTheId)
.ToList();
return result;
}
}
public class MyClass
{
public MyClass(string id)
{
ID = id;
Name = "My ID=" + id;
}
public string ID { get; set; }
public string Name { get; set; }
}
}