C# List<T> OrderBy float member - c#

I have a list that contain instances of class A.
class A
{
public int Id;
public float Value;
}
List<A> Collection = new List<A>( ... );
I want order the list using
Collection.OrderBy(item => item.Value);
This should be working but for float numbers it messes up the ordering. It will produce
1.0, 1.5, 1.6, 10.5, 11.54, 3.4, 4, 6.6, 7
Where 10.5, 11.54 should be at bottom the list.
This approach works perfectly for if Value were int. Any clue?

Without creating a new list:
Collection.Sort((x,y) => x.Value.CompareTo(y.Value));

Try
List<A> Collection = new List<A>( ... );
List<A> lstOrderedA = Collection.OrderBy(item => item.Value).ToList();
Here lstOrderedA will have the ordered list you are looking for.

i use this code :
List<A> Collection = new List<A>()
{
new A(){Id=1,Value=1.0f},new A(){Id=1,Value=11.5f},new A(){Id=1,Value=1.6f},new A(){Id=1,Value=10.5f}
};
List<A> orderedList = Collection.OrderBy(i =>i.Value).ToList();
and it show
1.0,1.6,10.5,11.5.

This proves that it does work: https://dotnetfiddle.net/3ryECS
Linq however doesn't change the original list it returns a new sorted IEnumerable<A>
So after calling .OrderBy(a => a.Value) the value of collection stays the same.
As explained in another answer if you want to change the original list you should use Sort
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
class A
{
public int Id;
public float Value;
}
public static void Main()
{
var collection = new List<A>{
new A { Id = 1, Value = 1.0f },
new A { Id = 5, Value = 5.0f },
new A { Id = 6, Value = 6.0f },
new A { Id = 10, Value = 10.0f },
new A { Id = 11, Value = 11.0f },
new A { Id = -289, Value = -289.0f },
new A { Id = 123, Value = 123.0f },
new A { Id = 3, Value = 3.0f }
};
foreach (var a in collection.OrderBy(v => v.Value))
{
Console.WriteLine(a.Value);
}
}
}
which outputs:
-289
1
3
5
6
10
11
123

try this :
List<A> ResultList = Collection.OrderBy(item => item.Value).ToList();
U can use Linq query syntax too:
IEnumerable<A> c = from x in Collection
orderby x.Value
select x;

Related

How to combine elements of list? (C#)

I am working in C#.
How to combine (sum, plus, minus) these class elements in both lists?
class Attribute
{
public AttributeType WhatAttri;
public float amount;
}
enum AttributeType{
maxhp, str, dex, int, wis,,,,
}
Attribute[] attList1;
Attribute[] attList2;
If specific values are like this,
attList1[0] = new Attribute(AttributeType.maxhp, 6)
attList1[1] = new Attribute(AttributeType.str, 4)
attList1[2] = new Attribute(AttributeType.dex, 3)
attList2[0] = new Attribute(AttributeType.str, 9)
attList2[1] = new Attribute(AttributeType.int, 7)
attList2[2] = new Attribute(AttributeType.wis, 5)
I want final result like this, (attList1 values are added, attList2 values are deducted, and also sum(or minus or plus) duplicated AttributeType)
So at above two lists, AttributeType.str is same, so deduct duplicated attList2[0]'s amount variable's value (9) from attList1[1]'s value (4)
and exclude this element from attList2.
So final result should be,
Attribute[] combinedList; (or List<Attribute> combinedList )
combinedList[0] = new Attribute(AttributeType.maxhp, 6)
combinedList[1] = new Attribute(AttributeType.str, -5) (4 - 9)
combinedList[2] = new Attribute(AttributeType.dex, 3)
combinedList[3] = new Attribute(AttributeType.int, -7)
combinedList[4] = new Attribute(AttributeType.wis, -5)
How to achieve this?
Thanks.
var result =
attList2.Select(a => new Attribute(a.WhatAttri, -a.amount)) // line 1
.Concat(attList1) // line 2
.GroupBy(a => a.WhatAttri) // line 3
.Select(g => new Attribute(g.Key, g.Sum(a => a.amount))); // line4
foreach(var a in result)
{
Console.WriteLine($"{a.WhatAttri}: {a.amount}");
}
You want to sum up the counts of the first list and subtract the amounts of the second list. So first I transform the second list to a new list with negative amounts (line 1). then the two lists are joined into one list (line 2).
Then the big line is grouped by type (line 3). and then you have a structure of Key and items, where you create new Attributes by using the key and the sum of the amounts (line 4).
Edit: replaced "Union" in line 2 by "Concat" to avoid dropping duplicate values in case there would be a custom comparer method in class Attribute
The main issue is that you didn't have positive or negative value in the attributes modifier. How whould you boost those attribute? Once it's fix the solution is easy
Add both list with concat, GroupBy AttributeType, and select the values.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var attributes = new Attribute[] {
new Attribute{WhatAttri=AttributeType.maxhp, amount=6 },
new Attribute{WhatAttri=AttributeType.str, amount=4 },
new Attribute{WhatAttri=AttributeType.dex, amount=3 },
};
//Attribute modifier has to be either positive or negative
var attributesModifier = new Attribute[] {
new Attribute{WhatAttri=AttributeType.str, amount=-9 },
new Attribute{WhatAttri=AttributeType.#int, amount=-7 },
new Attribute{WhatAttri=AttributeType.wis, amount=-5 },
};
var newAttributes = attributes
.Concat(attributesModifier)
.GroupBy(x => x.WhatAttri)
.Select(group =>
new Attribute {
WhatAttri = group.Key,
amount = group.Sum(g => g.amount)
});
newAttributes.Dump();
}
public class Attribute
{
public AttributeType WhatAttri { get; set; }
public float amount { get; set; }
}
public enum AttributeType
{
maxhp, str, dex, #int, wis
}
}
OnLine demo https://dotnetfiddle.net/3C7n7F
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public enum AttributeType
{
maxhp, str, dex, intel, wis,
}
[System.Serializable]
public class Attribute
{
public AttributeType WhatAttri;
public float amount;
public Attribute(AttributeType type, int a)
{
WhatAttri = type;
amount = a;
}
}
public class LinqTest : MonoBehaviour
{
Attribute[] attList1 = new Attribute[3];
Attribute[] attList2 = new Attribute[3];
void Start()
{
attList1[0] = new Attribute(AttributeType.maxhp, 6);
attList1[1] = new Attribute(AttributeType.str, 4);
attList1[2] = new Attribute(AttributeType.dex, 3);
attList2[0] = new Attribute(AttributeType.str, 9);
attList2[1] = new Attribute(AttributeType.intel, 7);
attList2[2] = new Attribute(AttributeType.wis, 5);
Calcul();
}
void Calcul()
{
var result = attList2
.Select(a => new Attribute(a.WhatAttri, -(int)a.amount)) // line 1
.Union(attList1) // line 2
.GroupBy(a => a.WhatAttri) // line 3
.Select(g => new Attribute(g.Key, g.Sum(a => (int)a.amount))); // line4
foreach (var a in result)
{
Debug.Log($"{a.WhatAttri}: {a.amount}");
}
}
}
This is final result of testing above code by answers.
Using Unity engine.

IEnumerable.Select() when attribute is known only at runtime

Say I have a data class like this and a list of its objects:
public class DataSet
{
public int A { get; set; }
public string B { get; set; }
public double C { get; set; }
}
var data = new List<DataSet>
{
new DataSet() { A = 1, B = "One", C = 1.1 },
new DataSet() { A = 2, B = "Two", C = 2.2 },
new DataSet() { A = 3, B = "Three", C = 3.3 }
};
I would like to do a Select() on the list, based on different properties. For example, if I need a list of property A, I could do this easily:
var listA = data.Select(x => x.A).ToList();
All good so far.
But in my program, I need to do the above, only, I wouldn't know whether I need a list of A or B or C until runtime. This 'knowledge' of what to select is stored in a list of strings, and I need to iterate it and extract only the appropriate lists. Something like this:
// GetKeys() will return the keys that I need to extract.
// So at one time keyList could have "A" and "B", another time "B" and "C" etc.
List<string> keyList = GetKeys();
foreach (var key in keyList)
{
// What do I do here?
data.Select(x =>???).ToList();
}
Is this possible at all? I'm fine with even a non-LINQ solution, if it achieves my goal.
EDIT:
Clarifying the requirement.
The end result I want is a separate list based on each 'key' mentioned above. So, something like
List<List<object>>
The count in outer list would be the count of keyList.
The inner list would have as many items as in DataSet.
This would probably not be the most efficient solution, but you could use Reflection for a fully dynamic solution:
private static List<List<object>> SelectDynamicData<T>(IEnumerable<T> data, List<string> properties)
{
// get the properties only once per call
// this isn't fast
var wantedProperties = typeof(T)
.GetProperties()
.Where(x => properties.Contains(x.Name))
.ToArray();
var result = new Dictionary<string, List<object>>();
foreach (var item in data)
{
foreach (var wantedProperty in wantedProperties)
{
if (!result.ContainsKey(wantedProperty.Name))
{
result.Add(wantedProperty.Name, new List<object>());
}
result[wantedProperty.Name].Add(wantedProperty.GetValue(item));
}
}
return result.Select(x => x.Value).ToList();
}
And, of course, you'd need to do a double foreach or a LINQ query to print that. For example:
var data = new List<DataSet>
{
new DataSet() { A = 1, B = "One", C = 1.1 },
new DataSet() { A = 2, B = "Two", C = 2.2 },
new DataSet() { A = 3, B = "Three", C = 3.3 }
};
var selectedData = SelectDynamicData(data, new List<string> { "A", "C" });
foreach (var list in selectedData)
{
foreach (object item in list)
{
Console.Write(item + ", ");
}
Console.WriteLine();
}
Using Creating Expression Trees by Using the API you can build an expression tree to represent the linq query you were hard coding in order to make it more dynamic.
Expression<Func<TModel, object>> GetPropertyExpression<TModel>(string propertyName) {
// Manually build the expression tree for
// the lambda expression v => v.PropertyName.
// (TModel v) =>
var parameter = Expression.Parameter(typeof(TModel), "v");
// (TModel v) => v.PropertyName
var property = Expression.Property(parameter, propertyName);
// (TModel v) => (object) v.PropertyName
var cast = Expression.Convert(property, typeof(object));
var expression = Expression.Lambda<Func<TModel, object>>(cast, parameter);
return expression;
}
Review the comments to understand the building of the expression tree.
This now can be used with the data to extract the desired result.
Following similar to what was provided in another answer it would be simplified to
List<List<object>> SelectDynamicData<T>(IEnumerable<T> data, List<string> properties) {
return properties
.Select(_ => data.Select(GetPropertyExpression<T>(_).Compile()).ToList())
.ToList();
}
Both methods are displayed in the following example
[TestMethod]
public void TestMethod1() {
var data = new List<DataSet>
{
new DataSet() { A = 1, B = "One", C = 1.1 },
new DataSet() { A = 2, B = "Two", C = 2.2 },
new DataSet() { A = 3, B = "Three", C = 3.3 }
};
var propertyKnownAtRuntime = "A";
var expression = GetPropertyExpression<DataSet>(propertyKnownAtRuntime);
var listA = data.Select(expression.Compile()).ToList();
//Produces
// { 1, 2, 3}
var listAC = SelectDynamicData(data, new List<string> { "A", "C" });
//Produces
//{
// { 1, 2, 3},
// { 1.1, 2.2, 3.3 }
//}
}
You can use reflection, for example
string key = "A";
var query = data.Select(x =>
{
var prop = x.GetType().GetProperty(key); //NOTE: if key does not exist this will return null
return prop.GetValue(x);
});
foreach (var value in query)
{
Console.WriteLine(value); //will print 1, 2, 3
}

c#: Move element whose ID is in array to top of list

In C#,I have List of Employee object. Employee class is
public class Employee
{
public int ID { get; set; }
public string Name { get; set; }
}
In List objected are sorted based on Employee.ID. I have an array of int which is basically Employee.ID which I want on top of the list and in list,order must remain same as in array.
If I hava input like this
List:
[
{ID:1,Name:A},
{ID:2,Name:B},
{ID:3,Name:AA},
{ID:4,Name:C},
{ID:5,Name:CD},
.
.
{ID:100,Name:Z}
]
and Array: {2,3,1}
Then I want Output List:
[
{ID:2,Name:B},
{ID:3,Name:AA},
{ID:1,Name:A},
{ID:4,Name:C},
{ID:5,Name:CD},
.
.
{ID:100,Name:Z}
]
And I have done this
foreach (int i in a)
{
list = list.OrderBy(x => x.ID != i).ToList();
}
//a is array
//list is List
Any better Solution.Thanks in advance.
After you got your list sorted based on the ID just iterate the array and move the elements. In order to do this you need to first remove and then insert the item at the correct position.
for(int i = 0; i < myArray.Length; i++)
{
var e = myList.Single(x => x.Id == myArray[i]);
myList.Remove(e);
myList.Insert(i, e);
}
You may also want to use SingleOrDefault instead of Single to verify that myList even contains the element with the current id, e.g. when your array contains [2, 3, 101]
To add another version to the mix. The complete sorting can be done in one go:
list = list.OrderBy(e=> {int i =Array.IndexOf(a, e.ID); return i == -1 ? int.MaxValue : i; }).ToList();
where list is the EmployeeList and a the indices array. (NB, the for loop is not needed, the above should do both sortings).
Inside the OrderBy callback, if the id is not inside a, int.MaxValue is returned to place it after the ones inside the array (a.Length would work as well). OrderBy should maintain the original order of the enumeration (list) for those elements that return the same value.
PS, if you want to sort first by index inside a and the rest on the ids (not necessarily the original order), you can use the following (as long as a.Length + largest ID < int.MaxValue) : list = list.OrderBy(e=> {int i =Array.IndexOf(a, e.ID); return i == -1 ? a.Length + e.ID : i; }).ToList();
Here's a way to do it in pure LINQ, without changing the original sequence.
Broken into steps to see what's going on.
public static void Main()
{
var employeeList = new List<Employee>()
{
new Employee(){ ID= 1,Name= "A"},
new Employee() { ID= 2,Name= "B"},
new Employee() { ID= 3,Name= "AA"},
new Employee() { ID= 4,Name= "C"},
new Employee() { ID= 5,Name= "CD"},
new Employee() { ID= 100,Name= "Z"}
};
var orderByArray = new int[] { 2, 3, 1, 100, 5, 4 };
var sortPos = orderByArray.Select((i, index) => new { ID = i, SortPos = index });
var joinedList = employeeList.Join(sortPos, e => e.ID, sp => sp.ID, (e, sp) => new { ID = e.ID, Name = e.Name, SortPos = sp.SortPos });
var sortedEmployees = joinedList.OrderBy(e => e.SortPos).Select(e => new Employee { ID = e.ID, Name = e.Name });
}
Try this using LINQ:
List<Employee> employees = ...
int[] ids = ...
var orderEmployees = ids.Select(id => employees.Single(employee => employee.ID == id))
.Concat(employees.Where(employee => !ids.Contains(employee.ID)).ToList();
Foreach id in ids array we will grab the matching employee and we will concat to it all the employees that their id does not exist in ids array.
I like to use a special Comparer for that, it seems clearer to me, though a bit more code. It hides the complexity of the sort in the comparer class, and then you can just call it with :
theList.OrderBy(x => x.id, new ListOrderBasedComparer(sortList));
It will sort according to any list passed to the comparer when instantiating, and will put elements not in the "known sort list" at the end.
You can of course adapt it to your special needs.
public class ListOrderBasedComparer: Comparer<int>
{
private List<int> sortList;
public ListOrderBasedComparer(List<int> sortList)
{
// if you want you can make constructor accept arrays and convert it
// (if you find that more convenient)
this.sortList = sortList;
}
public override int Compare(int x, int y)
{
var indexOfX = sortList.FindIndex(a => a == x);
var indexOfY = sortList.FindIndex(a => a == y);
// handle elements not in sortArray : if not in sort array always assume they should be "less than the others" and "equal between them".
if (indexOfX == -1 && indexOfY == -1) return 0;
if (indexOfY == -1) return -1;
if (indexOfX == -1) return 1;
// if elements are in sortArray (FindIndex returned other than -1), use usual comparison of index values
return indexOfX.CompareTo(indexOfY);
}
}
Example on how to use it, with Linq :
public class TestCompare
{
public void test ()
{
var myArray = new MyClass[]
{
new MyClass { id = 1, name = "A" },
new MyClass { id = 2, name = "B" },
new MyClass { id = 3, name = "C" },
new MyClass { id = 4, name = "D" },
new MyClass { id = 5, name = "E" },
new MyClass { id = 6, name = "F" },
};
var myArray2 = new MyClass[]
{
new MyClass { id = 1, name = "A" },
new MyClass { id = 2, name = "B" },
new MyClass { id = 0, name = "X" },
new MyClass { id = 3, name = "C" },
new MyClass { id = 4, name = "D" },
new MyClass { id = 23, name = "Z"},
new MyClass { id = 5, name = "E" },
new MyClass { id = 6, name = "F" },
};
var sortList = new List<int> { 2, 3, 1, 4, 5, 6 };
// good order
var mySortedArray = myArray.OrderBy(x => x.id, new ListOrderBasedComparer(sortList)).ToList();
// good order with elem id 0 and 23 at the end
var mySortedArray2 = myArray2.OrderBy(x => x.id, new ListOrderBasedComparer(sortList)).ToList();
}
}
public class MyClass
{
public int id;
public string name;
}

How to map two different lists to one list?

I have two lists of Generic types A and B:
public class A {
int type;
string params;
bool isActive;
}
public class B {
int type;
}
How could I map them into one list of type A where B.type == A.type (not A.type == B.type!!) using linq?
Instances of class B contain int values that can be deleted or added whereas instances of class A contain values from my db.
So for example:
A[0] = {1, "11", true}, A[1] = {2, "22", true}, A[2] = {3, "33", false}
and
B = {2, 3}
The desired result consists of A[1] and A[2].
It sounds like what you mean is "filter the items from the first list by checking a property against a second list" - in which case, I would suggest:
build an index from the second list:
// create an index of the "type"s to look for
var index = new HashSet<int>(bList.Select(x => x.type));
use this to filter the data
// filter the primary list to values from the index
var matches = aList.FindAll(x => index.Contains(x.type));
This will very efficiently give you a list of just the A data that has corresponding values in the bList.
Here it is runnable:
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
public class A
{
public int type;
public string #params;
public bool isActive;
}
public class B
{
public int type;
}
static void Main()
{
var aList = new List<A>
{
new A { type = 1, #params = "11", isActive = true },
new A { type = 2, #params = "22", isActive = true },
new A { type = 3, #params = "33", isActive = false },
};
var bList = new List<B>
{
new B { type = 2 },
new B { type = 3 },
};
// create an index of the "type"s to look for
var index = new HashSet<int>(bList.Select(x => x.type));
// filter the primary list to values from the index
var matches = aList.FindAll(x => index.Contains(x.type));
foreach (var match in matches)
{
Console.WriteLine($"{match.type}, {match.#params}, {match.isActive}");
}
}
}
with output:
2, 22, True
3, 33, False
You want to join both lists, so find all A which are in both lists?
var query = from a in aList
join b in bList
on a.type equals b.type
select a;
List<A> resultList = query.ToList();
Is this what you are looking for !?
var result = arrayA.Where(a => arrayB.Select(b => b.type).Contains(a.type)).ToArray();
If you have two sequences, where both sequence have a value that should match, and you want to take zero or more properties form the first sequence and zero or more properties from the second sequence you use Enumerable.Join.
The syntax seems a bit difficult, but if used more often you get accustomed to it.
Suppose in your example you have a sequence of A objects and a sequence of B objects:
IEnumerable<A> myAobjects = ...
IEnumerable<B> myBobjects = ...
// do the join:
myAObjects.Join(myBobjects, // join the two sequences
myAobject => myAobject.type, // from the A sequence take property type
myBobject => myBobject.type, // from the B sequence also take property type
(myAobject, myBobject) => // whenever the values of these properties equal, take:
...);
The dots will be filed with what you want from the combination of a myAobject and a myBobject that have the same value for property type. Your question is simple: whenever a myAobject.type matches a myBobject.type, you want the complete myAObject. In that case the last part of the join is:
(myAobject, myBobject) => myAobject
If you wanted something else returned you would use something like:
(myAobject, myBobject) => new
{
MyParams = myAobject.Params,
MyOtherValue = myBObject.type,
}

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

Categories

Resources