I am using the following code to return an IList:
FileName = Path.GetFileName(files[i]);
IList<DataX> QueryListFromFTP = DataX.GetListFromFTP(FileName);
QueryListFromFTP = (IList<DataX>)QueryListFromFTP
.Select(x => new { x.user_id, x.date, x.application_ID })
.ToList()
.Distinct();
However I keep getting this error:
Unable to cast object of type 'd__7a1[<>f__AnonymousType03[System.String,System.String,System.String]]' to type 'System.Collections.Generic.IList`1[DataXLibrary.DataX]'.
What am I doing wrong?
If what you want is a List < DataX > than all you need is:
IList<DataX> QueryListFromFTP = DataX.GetListFromFTP(FileName).Distinct().ToList();
// Use QueryListFromFTP here.
If you want a List of a different type of object as a result of your .Select, than you need to store the result in a List of object of that type i.e. anonymous if that's what you want.
The following line creates an anonymous type in c# which is not correspondent to the type Datax:
new { x.user_id, x.date, x.application_ID })
You should alter it to something like this:
Select(x => new Datax(){User_id = x.user_id, Date = x.date, Application = x.application_ID })
There are two problems in your code:
You're converting the List of DataX objects to an "anonymous type object" (the new { x.user_id, x.date, x.application_ID }). This object is not the same type as DataX, and it can't be coerced back to a DataX object automatically.
Trying to read between the lines a little, it looks like you want a distinct list of DataX objects, where distinctness is determined by a subset of the properties of a DataX object. So you have to answer the question, what will you do with duplicates (by this definition) that have different data in other properties? You have to discard some of them. Distinct() is not the right tool for this, because it only applies to the entire object of the IEnumerable it is applied to.
It's almost like you need a DistinctBy with one parameter giving the properties to calculate distinctness with, and a second parameter giving some logic for deciding which of the non-distinct "duplicates" to select. But this can be achieved with multiple IEnumerable methods: GroupBy and a further expression to select an appropriate single itemd from each resulting group. Here's one possible solution:
FileName = Path.GetFileName(files[i]);
IList<DataX> QueryListFromFTP = DataX.GetListFromFTP(FileName)
.GroupBy(datax => new { datax.user_id, datax.date, datax.application_ID })
.Select(g => g.First()); // or another expression to choose one item per group
.ToList();
If, for example, there were a version field and you wanted the most recent one for each "duplicate", you could:
.Select(g => g.OrderByDescending(datax => data.version).First())
Please note, however, that if you just want distinctness over all the properties of the object, and there is no need to select one particular value (in order to get its additional properties after throwing away some objects considered duplicates), then it may be as simple as this:
IList<DataX> QueryListFromFTP = DataX.GetListFromFTP(FileName)
.Distinct()
.ToList();
I would furthermore advise that you use IReadOnlyCollection where possible (that's .ToList().AsReadOnly()) and that, depending on your data, you may want to make the GetListFromFTP function perform the de-duplication/distinctness instead.
To answer any concerns that GroupBy isn't the right answer because it may not perform well enough, here is an alternate way to handle this (though I wholeheartedly disagree with you--until tests prove it's slow, it's a perfectly fine answer).
// in a static helper class of some kind
public static IEnumerable<T> DistinctBy<T, TKey>(
this IEnumerable<T> source,
Func<T, TKey> keySelector
) {
if (source == null) {
throw new ArgumentNullException("source", "Source enumerable cannot be null.");
}
if (keySelector == null) {
throw new ArgumentNullException("keySelector", "keySelector function cannot be null. To perform a generic distinct, use .Distinct().");
}
return DistinctByImpl(source, keySelector);
}
private static IEnumerable<T> DistinctByImpl<T, TKey>(
this IEnumerable<T> source,
Func<T, TKey> keySelector
) {
HashSet<TKey> keys = new HashSet<TKey>();
return source.Where(s => keys.Add(keySelector(s)));
}
It is used like this:
public class Animal {
public string Name { get; set; }
public string AnimalType { get; set; }
public decimal Weight { get; set; }
}
IEnumerable<Animal> animals = new List<Animal> {
new Animal { Name = "Fido", AnimalType = "Dog", Weight = 15.0M },
new Animal { Name = "Trixie", AnimalType = "Dog", Weight = 15.0M },
new Animal { Name = "Juliet", AnimalType = "Cat", Weight = 12.0M },
new Animal { Name = "Juliet", AnimalType = "Fish", Weight = 1.0M }
};
var filtered1 = animals.DistinctBy(a => new { a.AnimalType, a.Weight });
/* returns:
Name Type Weight
Fido Dog 15.0
Juliet Cat 12.0
Juliet Fish 1.0
*/
var filtered2 = animals.DistinctBy(a => a.Name); // or a simple property
/* returns:
Name Type Weight
Fido Dog 15.0
Trixie Dog 15.0
Juliet Cat 12.0
*/
Related
I have seen numerous articles which all seem to nibble around the issue I am having but none have provided an actual resolution to the problem. Which is to say they all get me to about 98% solution only to fail in some small detail.
I have an IEnumerable that is collected at run time. This IEnumerable could be of ANYTHING. I will not know until runtime. I need to sort it however based on a list of propertyNames and sort directions in a List of KeyValuePair objects provided as an argument.
public static void SortData(IEnumerable<dynamic> dataToSort, List<KeyValuePair<string, string>> sortArgs)
{
}
I first get the Type of the IEnumerable. I have created a method for this. I won't get into this detail here. It's been tested and does return the proper Type.
public static void SortData(IEnumerable<dynamic> dataToSort, List<KeyValuePair<string, string>> sortArgs)
{
Type dataType = TypeService.GetType(data);
}
I then attempt to create an initial IOrderedEnumerable that I can apply the sortArgs sort to.
public static void SortData(IEnumerable<dynamic> dataToSort, List<KeyValuePair<string, string>> sortArgs)
{
Type dataType = TypeService.GetType(dataToSort);
// Create IOrderedEnumerable
//
var query = from dataItem in dataToSort
orderby // ?????
select dataItem;
// apply sortArgs to IOrderedEnumerable
//
for(int argIDX = 0; argIDX < sortArgs.Count; argIDX++)
{
var arg = sortArgs[argIDX];
var sortField = arg.Key.Trim();
var sortDirection = arg.Value.Trim().ToUpper();
if(argIDX == 0)
{
if(sortDirection == "DESC")
{
query = query.OrderByDescending(e => e.GetType().GetProperty(sortField).GetValue(e));
}
else
{
query = query.OrderBy(e => e.GetType().GetProperty(sortField).GetValue(e));
}
}
else
{
if(sortDirection == "DESC")
{
query = query.ThenByDescending(e => e.GetType().GetProperty(sortField).GetValue(e));
}
else
{
query = query.ThenBy(e => e.GetType().GetProperty(sortField).GetValue(e));
}
}
}
// After applying the sort retreive the contents
//
dataToSort = query.ToList();
}
It seems by this point I have all the information I should need to sort the original dataToSort argument. But defining a property to initialize the IOrderedEnumerable is eluding me.
I have tried a number of different techniques I have read about ...
var query = from dataItem in dataToSort
orderby ( X => 1) //ERR: The type of one of the expressions in the OrderBy clause is incorrect. Type inference failed in the call to 'OrderBy'.
select dataItem;
I have tried to create a new Typed list (since I know the type) so that the expressions in the OrderBy clause could be inferred more accurately.
var typedDataToSort = new List<dataType>(); //ERR: dataType is a variable used like a type
foreach(var item in dataToSort)
{
typedDataToSort.Add( item );
}
I have tried to get the PropertyInfo for a property to sort on..
PropertyInfo propInfo = dataType.GetProperty(dataValueField);
var query = from dataItem in dataToSort
orderby (x => propInfo.GetValue(x, null)) //ERR: The type of one of the expressions in the OrderBy clause is incorrect. Type inference failed in the call to 'OrderBy'. Of couse this could not work since `dataType` is an INSTANCE of
select dataItem;
I have just run out of ideas.
To create an IOrderedIEnumerable from a List<T> without manipulating the order you can just execute a noop sort by doing something like this:
var myList = new List<String>(){"test1", "test3", "test2"}
IOrderedIEnumerable<String> query = myList.OrderBy(x => 1);
The same goes for an IEnumerable - however you must ensure that the underlaying type of IEnumerable provides stable order for each iteration.
I have an object of objects where one property can have duplicate values i am trying to find an algorithm to loop through the object and create a new list of object having group by duplicate values.
how do i loop through an object and create a new object in C# i have a view model eg:
pViewModel {
public itemFullName {get;set;}
public Item Item{get;set;}
public string itemAddress {get;set;}
public string itemCountry {get;set;}
public string addressId {get;set;}
}
public Item{
public int itemId{get;set;}
}
I want to create a new object after finding matching fullname but different id so my new object will have a list of itemFullName, item.itemid(pipedelimited values for all the items in the previous list),itemaddress, itemCountry in it.
Any help will be awesome. thank you
someone pointed out to this
var itemsAndIds = list
.GroupBy(m => m.itemFullName, m => m.Item.itemId)
.Select(g => new {ItemFullName = g.Key, ItemIds = string.Join("|", g)})
but now I need the new properties added to this object
This answer could help you:
https://stackoverflow.com/a/5232194/1341189
Given that ItemAddress and ItemCountry are always the same as ItemFullName in your example that means you can do something like this:
var itemsAndIds = list
.GroupBy(m => new {
ItemFullName = m.itemFullName,
ItemAddress = m.itemAddress,
ItemCoutnry = m.itemCountry },
m => m.Item.itemId)
.Select(g => new {
ItemFullName = g.Key.ItemFullName,
ItemIds = string.Join("|", g),
ItemAddress = g.Key.ItemAddress,
ItemCoutnry = g.Key.ItemCountry})
Also I would like to suggest that you read the Microsoft Naming Conventions.
Property Names should be written in PascalCase (same goes for Class Names).
https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/names-of-type-members
You could override bool Equals(Object obj) method in your object. This way you can group by whole items and then just select new ones. You will keep your comparation logic sealed in your object, co you can easily use build in mechanics like == comparation or whole linq magic without need of any hacks. this way your class is fully compatible with SOLID principle
keep in mind that to preserve consistency if you override Equals, it is also worth to override int GetHashCode() method. this way you keep consistency of comparartion (to be precised you need to be sure that tour implementation follows those three main rules):
a == a and a.Equals(a) should always be true (Reflexivity).
a == b, b == a, a.Equals(b) and b.Equals(a) should always give the same result. (Symmetry)
If a == b is true and b == c is true, then a == c should also be true (Transitivity). The same applies to a.Equals(b), b.Equals(c) and a.Equals(c).
I have a list of named objects:
class NamedObject {
public string name;
public int value;
public NamedObject(string name, int value) {
this.name = name;
this.value = value;
}
}
...
public static bool HasRelationship(NamedObject a, NamedObject b) {
return a.value == b.value;
}
...
var objs = new List<NamedObject>();
objs.Add(new NamedObject("D", 1));
objs.Add(new NamedObject("Z", 2));
objs.Add(new NamedObject("Y", 3));
objs.Add(new NamedObject("A", 2));
objs.Add(new NamedObject("C", 1));
objs.Add(new NamedObject("Z", 1));
of which I would like to sort by name, and then sub-sort by a boolean relationship. For the purposes of this example the boolean relationship is a.value == b.value.
Output List:
A (2)
Z (2)
C (1)
D (1)
Z (1)
Y (3)
So sort by name, group by boolean relationship, sort sub-group by name.
Edit:
The above is a simplification of the actual sorting, in my application the HasRelationship function determines whether two orientations have symmetry. The orientations are named so that they appear in a logical order within the editor interface.
Here is a visualisation:
http://pbrd.co/16okFxp
I'm confused by the question, I will try to be as clear as possible.
First it seems you want to sort NamedObjects by name, that's the clear and easy part. An order by should do the work.
And then you want to partiton it by an arbitrary predicate based on a pair of NamedObjects. I think that's the issue that arises the confusion.
The predicate you provide determines properties of pairs of NamedObjects, so now you are dealing with pairs. There's no unique answer to this question.
I understand you want to partition pairs by the predicate but you must understand that with a boolean partition you only will have two partitions ( relation is true or not), and no guaranteed order of values within the partition.
So at most you could get (sorted by name on the first term of the pair):
pair(A,Z)(true)
pair(A,C)(false)
pair(A,D)(false)
...
pair(C,D)(true)
...
The point is you can't order by pair relationship without implicitly deal with pairs. So to give you an answer I will assume:
Relation may not be symmetric
You want to sort by the first pair term name
With this context an answer could be. First get pairs.
var namedPairs = namedObjects.SelectMany(outerNamedObject =>
namedObjects.Select(innerNamedObject => new
{
First = outerNamedObject,
Second = innerNamedObject
}));
Then we do the grouping
var partitionedNamedPairs = namedPairs.GroupBy(pair =>
HasRelationship(pair.First, pair.Second));
After that, sort by first term name and then by the group key ( the relation partition )
var result = partitionedNamedPairs.SelectMany(
grouping => grouping.Select(pair => new { pair, key = grouping.Key }))
.OrderBy(keyedPair => keyedPair.pair.First.name)
.ThenBy(keyedPair => keyedPair.key);
You could then use select to remove the second term of the pair, but i don't see the point of that, because your provided predicate is binary.
I think you should join your list by itself since your HasRelationship method needs two objects.
var result = objs.OrderBy(x => x.name)
.Join(objs, _ => true, _ => true, (l, r) => new { l, r, rel = HasRelationship(l, r) })
.Where(x => x.rel)
.SelectMany(x=>new []{x.l,x.r})
.Distinct()
.ToList();
Although this returns the list you expect, I can not say I understand your requirements clearly.
The following solution is fully commented and hopefully will help future readers of this question to understand the sorting process which was required.
The answer by #QtX was nice and concise, though it seems that people were having difficulty understanding what I was actually requesting, so sorry about that guys!
Usage Example:
var sortedObjs = objs.SortAndGroupByRelationship(obj => obj.name, HasRelationship);
Extension method for sort and group:
public static IEnumerable<T> SortAndGroupByRelationship<T, TKey>(this IEnumerable<T> objs, Func<T, TKey> keySelector, Func<T, T, bool> relationship) where TKey : IComparable<TKey> {
// Group items which are related.
var groups = new List<List<T>>();
foreach (var obj in objs) {
bool grouped = false;
// Attempt to place named object into an existing group.
foreach (var group in groups)
if (relationship(obj, group[0])) {
group.Add(obj);
grouped = true;
break;
}
// Create new group for named object.
if (!grouped) {
var newGroup = new List<T>();
newGroup.Add(obj);
groups.Add(newGroup);
}
}
// Sort objects within each group by name.
foreach (var group in groups)
group.Sort( (a, b) => keySelector(a).CompareTo(keySelector(b)) );
// Sort groups by name.
groups.Sort( (a, b) => keySelector(a[0]).CompareTo(keySelector(b[0])) );
// Flatten groups into resulting array.
var sortedList = new List<T>();
foreach (var group in groups)
sortedList.AddRange(group);
return sortedList;
}
var sorted = objs.GroupBy(x => x.value, (k, g) => g.OrderBy(x => x.name))
.OrderBy(g => g.First().name)
.SelectMany(g => g);
Returns exactly what you want, without using HasRelationship method.
In my code I use a collection<T> as binding source for different controls (WPF/C#). The collection is created from the system every time the application loads. I don't have control on the collection and its order (items within the collection) are randomly for every launch of the app.
For UI reasons I need to allow to sort the collection, display it in a listview and keep the sorting when modified by the user (moveup and movedown buttons). Therefore my Idea was simple. I simply write the items comma-separated into a hidden string variable eg. "itemX,ItemY,ItemZ".
Now I need a function that sorts the collection based on the string. I was thinking of a few foreach loops, but I am sure there is a better way of sorting the collection.
sort the Items within collection<t> in the same order as represented by the string.
string correctItemOrder = "Application5, Application2, Application4, Application3".
Collection<T> has items with just the property name (e.g. "Applicaton3") but is sorted randomly. I want to sort the collection in the same order as the string.
T is an interface an I can access a property "Name" that has the value that is stored in the string eg. "ItemX".
Any cool snippets/functions?
Thanks
A couple of ideas...
Either way, you'll want your "item order string" as an array, so...
var sortOrder = correctItemOrder.Split(new[] { ", " }, StringSplitOptions.None);
Then one option is to order your collection by the order of items in sortOrder (meaning you have to traverse through half of sortOrder, on average, for each element in your collection):
var sortedCollection = new Collection<T>(collection.OrderBy(x => Array.IndexOf(sortOrder, x.Name)).ToList());
Another option is to create a dictionary of Name => item, then traverse sortOrder, selecting items from this dictionary as you go...
var dict = collection.ToDictionary(x => x.Name);
var sortedCollection = new Collection<T>(sortOrder.Select(x => dict[x]).ToList());
It's worth noting that if new items are added to the collection, but not sortOrder, the first snippet will place them at the start of the collection, whereas the second one will discard them entirely. Similarly if items are present in sortOrder but not the collection, the first snippet will ignore them, whereas the second one will throw an exception.
EDIT:
The third option, of course, is to create dictionary from sortOrder, and use that.
var dict = sortOrder.Select((x, i) => new { x, i }).ToDictionary(x => x.x, x => x.i);
var sortedCollection = new Collection<T>(collection.OrderBy(x => dict[x.Name]).ToList());
EDIT2:
As Enigmativity has pointed out, using lookups instead of dictionaries allows you to handle the cases where dictionary keys are missing very neatly.
The last example using this technique:
var lookup = sortOrder.Select((x, i) => new {x, i}).ToLookup(x => x.x, x => x.i);
var sortedCollection = new Collection<T>(collection.OrderBy(x => lookup[x.Name].DefaultIfEmpty(Int32.MaxValue).First()).ToList());
I think a comparer like this should do the job:
public interface INamed {
string Name {get;}
}
public class CustomComparer : Comparer<INamed> {
Dictionary<string, int> hash;
public CustomComparer( ) {
var tokens = "Application5, Application2, Application4, Application3"
.Split( ',' )
.Select( s => s.Trim( ) )
.ToArray( );
hash = Enumerable.Range(0, tokens.Length)
.ToDictionary( i => tokens[i] );
}
public override int Compare( INamed x, INamed y ) {
return hash[x.Name] - hash[y.Name];
}
public static readonly CustomComparer Default = new CustomComparer();
}
EDIT: I see that Collection has not order by itself, so is needed to build a wrapper
class SortableCollection<T> : System.Collections.ObjectModel.Collection<T>
{
public SortableCollection() : this(new List<T>()) {}
public SortableCollection(List<T> list) : base(list) {}
public virtual void Sort() { ((List<T>)Items).Sort(); }
}
class CustomSortableCollection<T> : SortableCollection<T> where T: INamed
{
public override void Sort() {
((List<INamed>)Items).Sort(CustomComparer.Default);
}
}
This way you can sort the colection when you need it doing:
your_collection.Sort();
You could do this:
var rank =
correctItemOrder
.Split(',')
.Select((x, n) => new { x = x.Trim(), n, })
.ToLookup(z => z.x, z => z.x);
var query =
from i in items
orderby rank[i.Name]
.DefaultIfEmpty(int.MaxValue)
.First()
select i;
This handles missing values in the correctItemOrder string too.
I have two collections of objects of different type. Lets call them type ALPHA and type BRAVO. Each of these types has a property that is the "ID" for the object. No ID is duplicated within the class, so for any given ID, there is at most one ALPHA and one BRAVO instance. What I need to do is divide them into 3 categories:
Instances of the ID in ALPHA which do not appear in the BRAVO collection;
Instances of the ID in BRAVO which do not appear in the ALPHA collection;
Instances of the ID which appear in both collections.
In all 3 cases, I need to have the actual objects from the collections at hand for subsequent manipulation.
I know for the #3 case, I can do something like:
var myCorrelatedItems = myAlphaItems.Join(myBravoItems, alpha => alpha.Id, beta => beta.Id, (inner, outer) => new
{
alpha = inner,
beta = outer
});
I can also write code for the #1 and #2 cases which look something like
var myUnmatchedAlphas = myAlphaItems.Where(alpha=>!myBravoItems.Any(bravo=>alpha.Id==bravo.Id));
And similarly for unMatchedBravos. Unfortunately, this would result in iterating the collection of alphas (which may be very large!) many times, and the collection of bravos (which may also be very large!) many times as well.
Is there any way to unify these query concepts so as to minimize iteration over the lists? These collections can have thousands of items.
If you are only interested in the IDs,
var alphaIds = myAlphaItems.Select(alpha => alpha.ID);
var bravoIds = myBravoItems.Select(bravo => bravo.ID);
var alphaIdsNotInBravo = alphaIds.Except(bravoIds);
var bravoIdsNotInAlpha = bravoIds.Except(alphaIds);
If you want the alphas and bravos themselves,
var alphaIdsSet = new HashSet<int>(alphaIds);
var bravoIdsSet = new HashSet<int>(bravoIds);
var alphasNotInBravo = myAlphaItems
.Where(alpha => !bravoIdsSet.Contains(alpha.ID));
var bravosNotInAlpha = myBravoItems
.Where(bravo => !alphaIdsSet.Contains(bravo.ID));
EDIT:
A few other options:
The ExceptBy method from MoreLinq.
The Enumerable.ToDictionary method.
If both types inherit from a common type (e.g. an IHasId interface), you could write your own IEqualityComparer<T> implementation; Enumerable.Except has an overload that accepts an equality-comparer as a parameter.
Sometimes LINQ is not the answer. This is the kind of problem where I would consider using a HashSet<T> with a custom comparer to reduce the work of performing set operations. HashSets are much more efficient at performing set operations than lists - and (depending on the data) can reduce the work considerably:
// create a wrapper class that can accomodate either an Alpha or a Bravo
class ABItem {
public Object Instance { get; private set; }
public int Id { get; private set; }
public ABItem( Alpha a ) { Instance = a; Id = a.Id; }
public ABItem( Bravo b ) { Instance = b; Id = b.Id; }
}
// comparer that compares Alphas and Bravos by id
class ABItemComparer : IComparer {
public int Compare( object a, object b ) {
return GetId(a).Compare(GetId(b));
}
private int GetId( object x ) {
if( x is Alpha ) return ((Alpha)x).Id;
if( x is Bravo ) return ((Bravo)x).Id;
throw new InvalidArgumentException();
}
}
// create a comparer based on comparing the ID's of ABItems
var comparer = new ABComparer();
var hashAlphas =
new HashSet<ABItem>(myAlphaItems.Select(x => new ABItem(x)),comparer);
var hashBravos =
new HashSet<ABItem>(myBravoItems.Select(x => new ABItem(x)),comparer);
// items with common IDs in Alpha and Bravo sets:
var hashCommon = new HashSet<Alpha>(hashAlphas).IntersectWith( hashSetBravo );
hashSetAlpha.ExceptWith( hashSetCommon ); // items only in Alpha
hashSetBravo.ExceptWith( hashSetCommon ); // items only in Bravo
Dictionary<int, Alpha> alphaDictionary = myAlphaItems.ToDictionary(a => a.Id);
Dictionary<int, Bravo> bravoDictionary = myBravoItems.ToDictionary(b => b.Id);
ILookup<string, int> keyLookup = alphaDictionary.Keys
.Union(bravoDictionary.Keys)
.ToLookup(x => alphaDictionary.ContainsKey(x) ?
(bravoDictionary.ContainsKey(x) ? "both" : "alpha") :
"bravo");
List<Alpha> alphaBoth = keyLookup["both"].Select(x => alphaDictionary[x]).ToList();
List<Bravo> bravoBoth = keyLookup["both"].Select(x => bravoDictionary[x]).ToList();
List<Alpha> alphaOnly = keyLookup["alpha"].Select(x => alphaDictionary[x]).ToList();
List<Bravo> bravoOnly = keyLookup["bravo"].Select(x => bravoDictionary[x]).ToList();
Here is one possible LINQ solution that performs a full outer join on both sets and appends a property to them showing which group they belong to. This solution might lose its luster, however, when you try to separate the groups into different variables. It all really depends on what kind of actions you need to perform on these objects. At any rate this ran at (I thought) an acceptable speed (.5 seconds) for me on lists of 5000 items:
var q =
from g in
(from id in myAlphaItems.Select(a => a.ID).Union(myBravoItems.Select(b => b.ID))
join a in myAlphaItems on id equals a.ID into ja
from a in ja.DefaultIfEmpty()
join b in myBravoItems on id equals b.ID into jb
from b in jb.DefaultIfEmpty()
select (a == null ?
new { ID = b.ID, Group = "Bravo Only" } :
(b == null ?
new { ID = a.ID, Group = "Alpha Only" } :
new { ID = a.ID, Group = "Both" }
)
)
)
group g.ID by g.Group;
You can remove the 'group by' query or create a dictionary from this (q.ToDictionary(x => x.Key, x => x.Select(y => y))), or whatever! This is simply a way of categorizing your items. I'm sure there are better solutions out there, but this seemed like a truly interesting question so I thought I might as well give it a shot!
I think LINQ is not the best answer to this problem if you want to traverse and compare the minimum amount of times. I think the following iterative solution is more performant. And I believe that code readability doesn't suffer.
var dictUnmatchedAlphas = myAlphaItems.ToDictionary(a => a.Id);
var myCorrelatedItems = new List<AlphaAndBravo>();
var myUnmatchedBravos = new List<Bravo>();
foreach (Bravo b in myBravoItems)
{
var id = b.Id;
if (dictUnmatchedAlphas.ContainsKey(id))
{
var a = dictUnmatchedAlphas[id];
dictUnmatchedAlphas.Remove(id); //to get just the unmatched alphas
myCorrelatedItems.Add(new AlphaAndBravo { a = a, b = b});
}
else
{
myUnmatchedBravos.Add(b);
}
}
Definition of AlphaAndBravo:
public class AlphaAndBravo {
public Alpha a { get; set; }
public Bravo b { get; set; }
}