How can i do this with Extensions methods or Linq?
It is a little hard to explain it with my poor english but i will try.
In below list sequence, if a item first field has same value with another item first field value but not same second fields. As result i want to collect items which has same first field but not second fields.
It looks quite easy but i think it is not any.Consider that you will work on same sequence so it is important doing it effectively.
class MyClass
{
public int first;
public int开发者_Go百科 second;
}
List<MyClass> sequence = new List<MyClass>();
Try this:
List<MyClass> sequence = new List<MyClass>()
{
new MyClass{ First = 1, Second = 10 },
new MyClass{ First = 1, Second = 10 },
new MyClass{ First = 2, Second = 11 },
new MyClass{ First = 2, Second = 12 }
};
var doesntMatch = sequence
.GroupBy(i => i.First)
.Select(g => new
{
Key = g.Key,
Values = g.Select(i => i.Second).Distinct()
})
.Where(i => i.Values.Count() > 1);
foreach (var i in doesntMatch)
{
Console.WriteLine(
"First = {0} contains {1} distinct values: {2}", i.Key, i.Values.Count(),
String.Join(", ", i.Values.Select(n => n.ToString()).ToArray()));
}
// output: "First = 2 contains 2 distinct values: 11, 12"
I'm thinking you might want to use GroupBy.
var sequence = new List<MyClass>()
{
new MyClass() { First = 1, Second = 2 },
new MyClass() { First = 1, Second = 3 },
new MyClass() { First = 1, Second = 4 },
new MyClass() { First = 3, Second = 2 },
new MyClass() { First = 5, Second = 4 },
};
var group1 = sequence.GroupBy(x => x.First);
you could do something like this with linq assuming you MyClass
objects are in some kind of collection
Let's say a list<MyClass> myList
for the example
(from o in myList where
(from o1 in myList where o1.first == o.first select o1).Count == 2
&& (from o2 in myList where o2.second == o.second select o2).count == 1
select o)
This says get all of the objects in my list where there are at least 2 objects that have the first parameter (o and some other object) and only one objects that have the second parameter.
I'm sure this could be improved upon.
I think that you could do this by joining the sequence to itself on the condition that the first
field is equal. Below is some example code that does this. The output is also shown below. Note that this code results in duplicate matches found, so you may have to address that.
class Program
{
class MyClass
{
public int ID;
public int first;
public int second;
}
static void Main(string[] args)
{
// create a sequence containing example data
List<MyClass> sequence = new List<MyClass>();
sequence.AddRange(new MyClass[] {
new MyClass { ID = 1, first = 0, second = 10 },
new MyClass { ID = 2, first = 1, second = 11 },
new MyClass { ID = 3, first = 2, second = 12 },
new MyClass { ID = 4, first = 0, second = 10 },
new MyClass { ID = 5, first = 1, second = 20 },
new MyClass { ID = 6, first = 2, second = 30 },
new MyClass { ID = 7, first = 0, second = 0 },
new MyClass { ID = 8, first = 1, second = 11 },
new MyClass { ID = 9, first = 2, second = 12 },
});
var matches = from x in sequence
join y in sequence // join sequence to itself
on x.first equals y.first // based on the first field
where
!object.ReferenceEquals(x, y) // avoid matching an item to itself
&& x.second != y.second // find cases where the second field is not equal
select new { X = x, Y = y }; // return a "tuple" containing the identified items
foreach (var match in matches)
{
Console.WriteLine("Found first:{0}, x.second:{1}, y.second:{2}, x.ID:{3}, y.ID:{4}", match.X.first, match.X.second, match.Y.second, match.X.ID, match.Y.ID);
}
}
}
The output of this program is the following:
Found first:0, x.second:10, y.second:0, x.ID:1, y.ID:7
Found first:1, x.second:11, y.second:20, x.ID:2, y.ID:5
Found first:2, x.second:12, y.second:30, x.ID:3, y.ID:6
Found first:0, x.second:10, y.second:0, x.ID:4, y.ID:7
Found first:1, x.second:20, y.second:11, x.ID:5, y.ID:2
Found first:1, x.second:20, y.second:11, x.ID:5, y.ID:8
Found first:2, x.second:30, y.second:12, x.ID:6, y.ID:3
Found first:2, x.second:30, y.second:12, x.ID:6, y.ID:9
Found first:0, x.second:0, y.second:10, x.ID:7, y.ID:1
Found first:0, x.second:0, y.second:10, x.ID:7, y.ID:4
Found first:1, x.second:11, y.second:20, x.ID:8, y.ID:5
Found first:2, x.second:12, y.second:30, x.ID:9, y.ID:6
Here's what I came up with:
class MyClass
{
public int First;
public int Second;
}
void Main()
{
List<MyClass> sequence = new List<MyClass>()
{
new MyClass{ First = 1, Second = 10 },
new MyClass{ First = 1, Second = 10 },
new MyClass{ First = 1, Second = 11 },
new MyClass{ First = 2, Second = 11 },
new MyClass{ First = 2, Second = 12 },
new MyClass{ First = 3, Second = 10 }
};
var lonelyItems = sequence
// remove all those which don't match First
.GroupBy(x => x.First).Where(g => g.Count() > 1)
// keep only one for each Second
.SelectMany(g => g.GroupBy(x => x.Second)).Select(g => g.First());
foreach (var x in lonelyItems)
Console.WriteLine(x);
// output:
// 1,10
// 1,11
// 2,11
// 2,12
}
精彩评论