开发者

How can i do this with Extensions methods or Linq?

开发者 https://www.devze.com 2023-01-06 03:56 出处:网络
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

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
}
0

精彩评论

暂无评论...
验证码 换一张
取 消