I have two lists, I need to find the items in the first list that are missing from the second, but I can only compare them with a Boolean function.
class A
{
internal bool Matching(A a)
{
return true;
}
}
class OuterMatch
{
List<A> List1 = new List<A>();
List<A> List2 = new List<A>();
void BasicOuterJoin()
{
// textbook example of an outer join, but it does not use my Matching function
var missingFrom2 = from one in List1
join two in List2
on one equals two into matching
from match in matching.DefaultIfEmpty()
where match == null
select one;
}
void Matching()
{
// simple use of the matching function, but this is an inner join.
var matching = from one in List1
from two in List2
where one.Matching(two)
select one;
}
void MissingBasedOnMatching()
{
// a reasonable substitute for what I'm after
var missingFrom2 = from one in List1
where (from two in List2
where two.Matching(one)
select two)
.Count() == 0
select one;
}
MissingBasedOnMatching gives me the right results, but it's not visually obviously an outer join like BasicOuterJoin is. Is there a clearer way to do this?
There's a form of GroupJoin that takes a comparison operator, but I'm not clear if there is a way to use it to make an outer join.
I've been using some useful (and short!) code from a blog by Ed Khoze.
He's posted a handy class which provides an adapter so that you can use Enumerable.Except() with a lambda.
Once you have that code, you can use Except() to solve your problem like so:
var missing = list1.Except(list2, (a, b) => a.Matching(b));
Here's a complete compilable sample. Credit to Ed Khoze for the LINQHelper class:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
class A
{
public int Value;
public bool Matching(A a)
{
return a.Value == Value;
}
public override string ToString()
{
return Value.ToString();
}
}
class Program
{
void test()
{
var list1 = new List<A>();
var list2 = new List<A>();
for (int i = 0; i < 20; ++i) list1.Add(new A {Value = i});
for (int i = 4; i < 16; ++i) list2.Add(new A {Value = i});
var missing = list1.Except(list2, (a, b) => a.Matching(b));
missing.Print(); // Prints 0 1 2 3 16 17 18 19
}
static void Main()
{
new Program().test();
}
}
static class MyEnumerableExt
{
public static void Print<T>(this IEnumerable<T> sequence)
{
foreach (var item in sequence)
Console.WriteLine(item);
}
}
public static class LINQHelper
{
private class LambdaComparer<T>: IEqualityComparer<T>
{
private readonly Func<T, T, bool> _lambdaComparer;
private readonly Func<T, int> _lambdaHash;
public LambdaComparer(Func<T, T, bool> lambdaComparer) :
this(lambdaComparer, o => 0)
{
}
private LambdaComparer(Func<T, T, bool> lambdaComparer, Func<T, int> lambdaHash)
{
if (lambdaComparer == null)
throw new ArgumentNullException("lambdaComparer");
if (lambdaHash == null)
throw new ArgumentNullException("lambdaHash");
_lambdaComparer = lambdaComparer;
_lambdaHash = lambdaHash;
}
public bool Equals(T x, T y)
{
return _lambdaComparer(x, y);
}
public int GetHashCode(T obj)
{
return _lambdaHash(obj);
}
}
public static IEnumerable<TSource> Except<TSource>
(
this IEnumerable<TSource> enumerable,
IEnumerable<TSource> second,
Func<TSource, TSource, bool> comparer
)
{
return enumerable.Except(second, new LambdaComparer<TSource>(comparer));
}
}
}
If your problem statement is actually
Find all members of X that do not exist in Y
And given a class Foo that implements IEquatable<Foo> (pretty much what your Matching method does):
class Foo : IEquatable<Foo>
{
public bool Equals( Foo other )
{
throw new NotImplementedException();
}
}
Then this code should give you what you want:
List<Foo> x = GetFirstList() ;
List<Foo> y = GetSecondList() ;
List<Foo> xNotInY = x.Where( xItem => ! y.Any( yItem => xItem.Equals(yItem) ) ).ToList() ;
You should bear in mind that this runs in O(N2) time. Consequently, you might want to implement an IEqualityComparer<Foo> and put your second list in a HashSet<Foo>:
class FooComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo x, Foo y)
{
if ( x == null )
{
return y == null ;
}
else if ( y == null ) return false ;
else
{
return x.Equals(y) ;
}
}
public int GetHashCode(Foo obj)
{
return obj.GetHashCode() ;
}
}
Then do something like
List<Foo> x = GetFirstList() ;
List<Foo> y = GetSecondList() ;
HashSet<Foo> yLookup = new HashSet<Foo>( y , new FooComparer() ) ;
List<Foo> xNotInY = x.Where( x => !yLookup.Contains(x) ) ;
You'll incur some overhead in constructing the hash set (1 pass through the second list), but subsequent lookups via Contains() are O(1).
If you look at the sources for the Linq join operation, this is close to what it does.
It wouldn't be difficult to strip the Linq sources for Join() and it's helpers and tweak them to product left and right join operators instead of the stock inner join.
Does this work for your purposes?
var missing = List1.Except(List2);
If you need custom comparison logic you can build a custom IEqualityComparer. Note however that Except treats both lists as sets, so it will eliminate duplicates from List1.
Related
apologises if the question is dumb, by I've want to know something to simplify my code:
I have a few lists:
public struct structure
{
public string item1;
public string item2;
public string item3;
public string item4;
}
public List <structure> listOfStruct=new List <structure>();
public List <string> lista;
public List <string> listb;
With below code I use the LINQ join to find matches in both lists:
query = from x in listOfStruct
join y2 in lista on x.item1 equals y2
select new
{
x.action
};
foreach (var item in query)
{
Answer = item.answer;
}
The variable 'Answer' then is used for further processing.
The thing is that somewhere else in my code I need to match another list with listOfStruct, but with another item of the struct:
query = from x in listOfStruct
join y2 in listB on x.item2 equals y2
select new
{
x.action
};
foreach (var item in query)
{
Answer = item.answer;
}
I would like to write a function something like this
public string matchList(string actionListItem, string [] second_list){
query = from x in listOfStruct
join y2 in second_list on x.actionListItem equals y2
select new
{
x.action
};
foreach (var item in query)
{
Answer = item.answer;
}
return Answer;
}
and call if from anywhere in my program with something like below to be flexible and not write the same piece of code over and over again:
var action= matchList(string itemInlistOfStruct,string []secondList)
where "itemInListOfStruct" could be item1, item2...or item4 and "seconList" lista or listb.
Is this possible?
You must define the comparison with Strings for your structure. Use the following procedure to do this
public struct structure
{
public string item1;
public string item2;
public string item3;
public string item4;
public override bool Equals(string obj)
{
return obj==item1;//your code for check Equals
}
}
Whatever you do with matching elements of listOfStruct (which indeed is very unclear in your question), filtering out those matches is independent from processing them consecutively. So I'd suggest to split the matching and the further processing into two simpler steps.
I'd prefer to use a class instead of a struct in this case, so i rename your structstructure into the class Thing, also changing the type of Item2 for demonstration purposes:
public sealed class Thing
{
public string Item1 { get; set; }
public int Item2 { get; set; }
// ...
}
Now make some extension methods as you need:
public static class MyExtensions
{
public static IEnumerable<TSource> MatchBy<TSource, TItem>(this IEnumerable<TSource> source, Func<TSource, TItem> keySelector, IEnumerable<TItem> keys)
{
var keySet = keys as ISet<TItem> ?? new HashSet<TItem>(keys);
return source.Where(x => keySet.Contains(keySelector(x)));
// or equivalently, if you prefer
// return from x in source where keySet.Contains(keySelector(x)) select x;
}
public static string FurtherProcessing(this IEnumerable<Thing> things) => $"Number of things: {things.Count()}.";
}
And use them like this:
var things = new List<Thing>
{
new Thing { Item1 = "Foo", Item2 = 0 },
new Thing { Item1 = "Bar", Item2 = 1 },
new Thing { Item1 = "Bla", Item2 = 2 },
};
var answerByItem1 = things.MatchBy(thing => thing.Item1, new[] { "Foo", "Bar" }).FurtherProcessing();
var answerByItem2 = things.MatchBy(thing => thing.Item2, new[] { 1 }).FurtherProcessing();
If all of your ItemXXX are strings and you really need to identify them by name, you can add another overload of MatchBy:
public static IEnumerable<Thing> MatchBy(this IEnumerable<Thing> source, string keyName, IEnumerable<string> keys)
{
switch (keyName)
{
case nameof(Thing.Item1): return source.MatchBy(x => x.Item1, keys);
// case ...
default: throw new ArgumentOutOfRangeException(nameof(keyName));
}
}
I have an NUnit unit test which I have two collections of different types which I want to assert are equivalent.
class A { public int x; }
class B { public string y; }
[Test]
public void MyUnitTest()
{
var a = GetABunchOfAs(); // returns IEnumerable<A>
var b = GetABunchOfBs(); // returns IEnumerable<B>
Assert.IsPrettySimilar(a, b, (argA, argB) => argA.ToString() == argB);
}
where Assert.IsPrettySimilar is defined like such
public static void IsPrettySimilar<T1, T2>(
IEnumerable<T1> left,
IEnumerable<T2> right,
Func<T1, T2, bool> predicate)
{
using (var leftEnumerator = left.GetEnumerator())
using (var rightEnumerator = right.GetEnumerator())
{
while (true)
{
var leftMoved = leftEnumerator.MoveNext();
if (leftMoved != rightEnumerator.MoveNext())
{
Assert.Fail("Enumerators not of equal size");
}
if (!leftMoved)
{
break;
}
var isMatch = predicate(leftEnumerator.Current,
rightEnumerator.Current);
Assert.IsTrue(isMatch);
}
}
}
My question is, is there a more idiomatic way of doing the above with the existing methods in NUnit? I already looked at CollectionAssert and there's nothing matching what I want to do.
My description of "equivalent" in this case is:
1) Collections must be of same size
2) Collections must be in same logical order
3) Some predicate must be used to determine equivalence between matching items.
Let's think what you are trying to test. You are not trying to test that objects from first sequence are same as objects from second sequence. They can be very different. So, word similar is very vague here. What you really trying to test here, is that some projection of first sequence is equal to other projection of second sequence. And NUnit already have such functionality:
CollectionAssert.AreEqual(bunchOfAs.Select(a => a.ToString()),
bunchOfBs.Select(b => b));
Thus you are projecting both sequences to get particular data, then you can give nice names for these two projections, which will make your test readable to others. You have some hidden business logic here, which does not have explanation in code - you don't explain why you making such projections. So, nice names of projection results will explain your intent. E.g:
var expectedNames = employees.Select(u => u.Login);
var actualNames = customers.Select(c => c.Name);
CollectionAssert.AreEqual(expectedNames, actualNames);
That is much cleaner for me than
Assert.IsPrettySimilar(employees, customers, (e, c) => u.Login == c.Name);
I know you looked into CollectionAssert, however, I have found a strategy like this very useful in my own tests.
Overriding ToString and Equals on the objects makes this test pass.
[TestFixture]
public class Class1
{
[Test]
public void MyUnitTest()
{
var a = GetABunchOfAs(); // returns IEnumerable<A>
var b = GetABunchOfBs(); // returns IEnumerable<B>
CollectionAssert.AreEqual(a, b.OrderBy(x => x.y));
}
public List<A> GetABunchOfAs()
{
return new List<A>
{
new A() {x = 1},
new A() {x = 2},
new A() {x = 3},
new A() {x = 4}
};
}
public List<B> GetABunchOfBs()
{
return new List<B>
{
new B() {y = "4"},
new B() {y = "1"},
new B() {y = "2"},
new B() {y = "3"},
};
}
}
public class A
{
public int x;
public override bool Equals(object obj)
{
return obj.ToString().Equals(x.ToString());
}
public override string ToString()
{
return x.ToString();
}
}
public class B
{
public string y;
public override string ToString()
{
return y;
}
public override bool Equals(object obj)
{
return obj.ToString().Equals(y);
}
}
I deliberately left GetABunchOfBs out of order, however the test still passes.
It looks like Sergey's answer is the one I'm looking for (which was to see whether NUnit already has a facility to do what I want). However, this is the solution I ended up with, which is closer to the implementation I want.
public static class EnumerableAssert
{
public static void AreEquivilent<TExpected, TActual>(IEnumerable<TExpected> expected, IEnumerable<TActual> actual, Func<TExpected, TActual, bool> predicate)
{
if (ReferenceEquals(expected, actual))
{
return;
}
using (var expectedEnumerator = expected.GetEnumerator())
using (var actualEnumerator = actual.GetEnumerator())
{
while (true)
{
var expectedMoved = expectedEnumerator.MoveNext();
if (expectedMoved != actualEnumerator.MoveNext())
{
Assert.Fail("Expected and Actual collections are of different size");
}
if (!expectedMoved)
{
return;
}
Assert.IsTrue(predicate(expectedEnumerator.Current, actualEnumerator.Current));
}
}
}
}
i have a C# application in which i'd like to get from a List of Project objects , another List which contains distinct objects.
i tried this
List<Project> model = notre_admin.Get_List_Project_By_Expert(u.Id_user);
if (model != null) model = model.Distinct().ToList();
The list model still contains 4 identical objects Project.
What is the reason of this? How can i fix it?
You need to define "identical" here. I'm guessing you mean "have the same contents", but that is not the default definition for classes: the default definition is "are the same instance".
If you want "identical" to mean "have the same contents", you have two options:
write a custom comparer (IEqualityComparer<Project>) and supply that as a parameter to Distinct
override Equals and GetHashCode on Project
There are also custom methods like DistinctBy that are available lots of places, which is useful if identity can be determined by a single property (Id, typically) - not in the BCL, though. But for example:
if (model != null) model = model.DistinctBy(x => x.Id).ToList();
With, for example:
public static IEnumerable<TItem>
DistinctBy<TItem, TValue>(this IEnumerable<TItem> items,
Func<TItem, TValue> selector)
{
var uniques = new HashSet<TValue>();
foreach(var item in items)
{
if(uniques.Add(selector(item))) yield return item;
}
}
var newList =
(
from x in model
select new {Id_user= x.Id_user}
).Distinct();
or you can write like this
var list1 = model.DistinctBy(x=> x.Id_user);
How do you define identical? You should override Equals in Project with this definition (if you override Equals also override GetHashCode). For example:
public class Project
{
public int ProjectID { get; set; }
public override bool Equals(object obj)
{
var p2 = obj as Project;
if (p2 == null) return false;
return this.ProjectID == m2.ProjectID;
}
public override int GetHashCode()
{
return ProjectID;
}
}
Otherwise you are just checking reference equality.
The object's reference aren't equal. If you want to be able to do that on the entire object itself and not just a property, you have to implement the IEqualityComparer or IEquatable<T>.
Check this example: you need to use either Comparator or override Equals()
class Program
{
static void Main( string[] args )
{
List<Item> items = new List<Item>();
items.Add( new Item( "A" ) );
items.Add( new Item( "A" ) );
items.Add( new Item( "B" ) );
items.Add( new Item( "C" ) );
items = items.Distinct().ToList();
}
}
public class Item
{
string Name { get; set; }
public Item( string name )
{
Name = name;
}
public override bool Equals( object obj )
{
return Name.Equals((obj as Item).Name);
}
public override int GetHashCode()
{
return Name.GetHashCode();
}
}
Here's an answer from basically the same question that will help.
Explanation:
The Distinct() method checks reference equality for reference types. This means it is looking for literally the same object duplicated, not different objects which contain the same values.
Credits to #Rex M.
Isn't simpler to use one of the approaches shown below :) ?
You can just group your domain objects by some key and select FirstOrDefault like below.
More interesting option is to create some Comparer adapter that takes you domain object and creates other object the Comparer can use/work with out of the box. Base on the comparer you can create your custom linq extensions like in sample below. Hope it helps :)
[TestMethod]
public void CustomDistinctTest()
{
// Generate some sample of domain objects
var listOfDomainObjects = Enumerable
.Range(10, 10)
.SelectMany(x =>
Enumerable
.Range(15, 10)
.Select(y => new SomeClass { SomeText = x.ToString(), SomeInt = x + y }))
.ToList();
var uniqueStringsByUsingGroupBy = listOfDomainObjects
.GroupBy(x => x.SomeText)
.Select(x => x.FirstOrDefault())
.ToList();
var uniqueStringsByCustomExtension = listOfDomainObjects.DistinctBy(x => x.SomeText).ToList();
var uniqueIntsByCustomExtension = listOfDomainObjects.DistinctBy(x => x.SomeInt).ToList();
var uniqueStrings = listOfDomainObjects
.Distinct(new EqualityComparerAdapter<SomeClass, string>(x => x.SomeText))
.OrderBy(x=>x.SomeText)
.ToList();
var uniqueInts = listOfDomainObjects
.Distinct(new EqualityComparerAdapter<SomeClass, int>(x => x.SomeInt))
.OrderBy(x => x.SomeInt)
.ToList();
}
Custom comparer adapter:
public class EqualityComparerAdapter<T, V> : EqualityComparer<T>
where V : IEquatable<V>
{
private Func<T, V> _valueAdapter;
public EqualityComparerAdapter(Func<T, V> valueAdapter)
{
_valueAdapter = valueAdapter;
}
public override bool Equals(T x, T y)
{
return _valueAdapter(x).Equals(_valueAdapter(y));
}
public override int GetHashCode(T obj)
{
return _valueAdapter(obj).GetHashCode();
}
}
Custom linq extension (definition of DistinctBy extension method):
// Embedd this class in some specific custom namespace
public static class DistByExt
{
public static IEnumerable<T> DistinctBy<T,V>(this IEnumerable<T> enumerator,Func<T,V> valueAdapter)
where V : IEquatable<V>
{
return enumerator.Distinct(new EqualityComparerAdapter<T, V>(valueAdapter));
}
}
Definition of domain object used in test case:
public class SomeClass
{
public string SomeText { get; set; }
public int SomeInt { get; set; }
}
List<ViewClReceive> passData = (List<ViewClReceive>)TempData["passData_Select_BankName_List"];
passData = passData?.DistinctBy(b=>b.BankNm).ToList();
It will Works ......
Given two collections A & B, I want to output:
1. their inner join (say on a field called Id)
2. those elements in A that could not be found in B
3. those elements in B that could not be found in A
What is the most efficient way to do this?
When I say those elements in A that could not be found in B, I mean those elements that could not be "inner-joined" with B
For the inner join, have a look at the .Join() extension method: http://msdn.microsoft.com/en-us/library/bb344797.aspx
For the second 2 outputs, have a look at the .Except() extension method. http://msdn.microsoft.com/en-us/library/bb300779.aspx
For examples of most of the LINQ queries, have a look at this page: http://msdn.microsoft.com/en-us/vcsharp/aa336746
I guess I'd write this:
public class DeltaSet<T>
{
public ISet<T> FirstItems { get; private set; }
public ISet<T> SecondItems { get; private set; }
public ISet<Tuple<T, T>> IntersectedItems { get; private set; }
// T is the type of the objects, U is the key used to determine equality
public static DeltaSet<T> GetDeltaSet<T, U>(IDictionary<U, T> first,
IDictionary<U, T> second)
{
var firstUniques = new HashSet<T>(
first.Where(x => !second.ContainsKey(x.Key)).Select(x => x.Value));
var secondUniques = new HashSet<T>(
second.Where(x => !first.ContainsKey(x.Key)).Select(x => x.Value));
var intersection = new HashSet<Tuple<T, T>>(
second.Where(x => first.ContainsKey(x.Key)).Select(x =>
Tuple.Create(first[x.Key], x.Value)));
return new DeltaSet<T> { FirstItems = firstUniques,
SecondItems = secondUniques,
IntersectedItems = intersection };
}
public static DeltaSet<IDClass> GetDeltas(IEnumerable<IDClass> first,
IEnumerable<IDClass> second)
{
return GetDeltaSet(first.ToDictionary(x => x.ID),
second.ToDictionary(x => x.ID));
}
}
Assuming you have class A for elements in collection A and class B in collection B
class AB {
public A PartA;
public B PartB;
// Constructor
};
public void ManyJoin (List<A> colA, List<B> colB)
{
List<AB> innerJoin = new List<AB>();
List<A> leftJoin = new List<A>();
List<B> rightJoin = new List<B>();
bool[] foundB = new bool[colB.Count];
foreach (A itemA in colA)
{
int i = colB.FindIndex(itemB => itemB.ID == itemA.ID);
if (i >= 0)
{
innerJoin.Add (new AB(itemA, colB[i]));
foundB[i] = true;
}
else
leftJoin.Add(itemA);
}
for (int j = 0; j < foundB.count; j++)
{
if (!foundB[j])
rightJoin.Add(colB[j]);
}
}
This is one possible way. Whether it is optimum or not, I'm not sure, it does the job.
I'm looking for a creative pattern to enumerate two IEnumerable<>'s synchronized.
If I was making something up and adding to the C# syntax I might write:
foreach(var firstItem, var secondItem in this.ListOne, this.ListTwo)
{
if (firstItem.Prop == secondItem.Prop)
WorkSomeMagic(secondItem);
DoSomethingElse(firstItem);
}
Now, obviously that doesn't exist. What patterns have people used to accomplish something similar when dealing with enumerations that aren't accessible by index? Keep in mind, what is inside my pseudo-foreach would be more complex; I simplified for the example.
You're looking for Zip, which is new in .NET 4 or you can use the implementation here:
Is there a zip-like method in .Net?
I usually do the following:
using (IEnumerator<int> e1 = this.ListOne.GetEnumerator(),
e2 = this.ListTwo.GetEnumerator()) {
while (e1.MoveNext() && e2.MoveNext()) {
...
}
}
Or write an extension method:
public static void EnumerateWith<T>(this IEnumerable<T> left,
IEnumerable<T> right, Action<T,T> del) {
using (IEnumerator<T> l = left.GetEnumerator(),
r = right.GetEnumerator()) {
while (l.MoveNext() && r.MoveNext()) {
del(l.Current,r.Current);
}
}
}
ListOne.EnumerateWith(ListTwo, (left, right) => {
...
});
As with any general C# question, this will probably have 10 good answers posted before VS2008 even loads. Instead of that rat race, I'll come up with an offbeat "anti-pattern" you should never use. In fact, anyone writing mission critical code, please stop reading now.
using System;
using System.Collections.Generic;
using System.Linq;
class EnumTwoLists
{
static void Main(string[] args)
{
var left = new List<int>();
var right = new List<DateTime>();
var demo = new LinqAbuse<int, DateTime>(left, right);
demo.Populate(40, s => s * s, d => new DateTime(2009, d / 31 + 1, d % 31 + 1));
demo.Enumerate( (s, d) => Console.WriteLine(String.Format("Executing arbitrary code with {0} and {1}", s, d)) );
}
}
class LinqAbuse<T1, T2>
{
public LinqAbuse(List<T1> l, List<T2> r)
{
left = l;
right = r;
}
List<T1> left;
List<T2> right;
public void Populate(int size, Func<int, T1> leftGenerator, Func<int, T2> rightGenerator)
{
new int[size].Aggregate(0, (index, empty) => PopulateWrapper(left, right, leftGenerator, rightGenerator, index));
}
int PopulateWrapper(List<T1> left, List<T2> right, Func<int, T1> leftGenerator, Func<int, T2> rightGenerator, int index)
{
left.Add(leftGenerator(index));
right.Add(rightGenerator(index));
return ++index;
}
public void Enumerate(Action<T1, T2> loopBody)
{
left.Join(right, l => "", r => "",
(l, r) => ActionWrapper(l, r, loopBody),
new CartesianComparer<object>(right.Count))
.ToList();
}
object ActionWrapper(T1 x, T2 y, Action<T1, T2> action)
{
action(x, y);
return null;
}
}
class CartesianComparer<T> : IEqualityComparer<T>
{
public CartesianComparer(int _size)
{
size = _size;
equalsCounter = (size * (size - 1) >> 1) + size; // Combinations(size, 2) + (size - trueCounter)
}
private int size;
private int equalsCounter;
private int trueCounter = 0;
public bool Equals(T x, T y)
{
if (0 < --equalsCounter)
return false;
equalsCounter = size - ++trueCounter;
return true;
}
public int GetHashCode(T obj)
{
return 0;
}
}
Aww, isn't she cute? (alternate caption: Mommy, why is Anders crying?)
Ignoring checks for nulls and whatnot:
IEnumerable<T1> first;
IEnumerable<T2> second;
using (IEnumerator<T1> e1 = first.GetEnumerator()) {
using (IEnumerator<T2> e2 = second.GetEnumerator()) {
while (e1.MoveNext() && e2.MoveNext()) {
// do something eith e1.Current and e2.Current
}
}
}
I know this question is old but for anyone coming to this question now you can build on Jason's answer and JaredPar's answer with C#7's ValueTuple, which will give you a syntax similar to the original question. You may need to install the nuget package System.ValueTuple.
If you declare an extension method something like this:
internal static class EnumerableExtensions
{
internal static IEnumerable<(T1, T2)> EnumerateWith<T1, T2>(this IEnumerable<T1> first, IEnumerable<T2> second)
{
using (var firstEnumerator = first.GetEnumerator())
using (var secondEnumerator = second.GetEnumerator())
{
while(firstEnumerator.MoveNext() && secondEnumerator.MoveNext())
{
yield return (firstEnumerator.Current, secondEnumerator.Current);
}
}
}
}
Then you can use it like this:
List<Foo> foos = new List<Foo>()
{
new Foo(),
new Foo(),
new Foo()
};
List<Bar> bars = new List<Bar>()
{
new Bar(),
new Bar(),
new Bar()
};
foreach((Foo foo, Bar bar) in foos.EnumerateWith(bars))
{
Console.WriteLine(foo.ID);
Console.WriteLine(bar.ID);
}