views:

116

answers:

5

Hello

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 second;
}
List<MyClass> sequence = new List<MyClass>();
A: 

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.

msarchet
Group them by First, regroup by Second. Remove all groups larger than two of the second set. Should do the same I guess.
Claus Jørgensen
Yea it would do the same, I don't know if it's much clearer.
msarchet
+1  A: 

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);
Claus Jørgensen
+6  A: 

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"
Rubens Farias
This looks right one but i still need time to understand and control it.
Freshblood
You start grouping by `first`, then select your key value (ie, you unique `first` value) and list your second values.
Rubens Farias
I tested and it works as i expected.
Freshblood
You grabbed the question very well.
Freshblood
Ty! In return, you could to improve your accept rate; I'm sure you'll help many other ppl this way ;)
Rubens Farias
I don't consider accept rate or any other ratings while asking or helping .
Freshblood
Neither do I; ty anyways
Rubens Farias
A: 

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
}
Porges
output expected for my question is (1,10),(1,11),(2,11),(2,12)
Freshblood
There are two items with `1,10`, so I thought they shouldn't be output, because both have `Second = 10`... maybe I have understood your problem incorrectly. Is it that you want only *one* output for any particular `Second`, not to remove all duplicates?
Porges
If that's right then this should be:`/* keep only one of them for each Second */ .SelectMany(g => g.GroupBy(x => x.Second)).Select(x => x.First());`
Porges
(1,10) and (1,11) is fit for my criteria . U can check accepted answer.
Freshblood
A: 

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

Dr. Wily's Apprentice