Reputation: 3278
I have been trying to implement the below foreach to linq , but couldn't get my head around doing it. Can anyone give me pointers on how I can go about changing this to linq or in general on how to think in terms of linq rather than using for loops.
class Program
{
static void Main(string[] args)
{
List<Sales> S1 = new List<Sales>();
S1.Add(new Sales(1,1,false));
S1.Add(new Sales(1, 1, false));
S1.Add(new Sales(2, 2, false));
S1.Add(new Sales(3, 3, false));
S1.Add(new Sales(4, 4, false));
List<Sales> S2 = new List<Sales>();
S2.Add(new Sales(3, 3, false));
S2.Add(new Sales(4, 4, false));
//if S1 Product1 == S2 Product1 and S1 Product2 == S2 Product2 then S1 isSold == true.
for(int i = 0; i < S2.Count; i++)
{
for(int j = 0; j < S1.Count; j++)
{
if(S1[j].Product1 == S2[i].Product1 && S1[j].Product2 == S2[i].Product2)
{
S1[j].ISSold = true;
}
}
}
}
public class Sales
{
public int Product1 { get; set; }
public int Product2 { get; set; }
public bool ISSold { get; set; }
public Sales(int product1, int product2, bool iSSold)
{
Product1 = product1;
Product2 = product2;
ISSold = iSSold;
}
}
}
}
Upvotes: 1
Views: 9795
Reputation: 622
It's normally considered bad practice to have side-effects within a Linq query, so I'd recommend using a structure of
foreach(var item in /*query*/)
item.IsSold = true;
If S1 and S2 may be very large in the real use case, the current method of intersecting them is very slow - O(n^2) because it iterates the whole of S2 for each element of S1, and the other answers simply translate this poor performance into a Linq calling syntax.
Linq has a function for doing an intersection, so if S1 and S2 may be quite large, then using this will make your intentions clearer and also allow for improved performance with large sets.
The nested for and if loops can be rewritten as:
foreach (Sales item in S1.Intersect(S2, new SalesProductComparer()))
item.ISSold = true;
... if you define a helper class like
public class SalesProductComparer : IEqualityComparer<Sales>
{
public bool Equals(Sales x, Sales y)
{
return x.Product1 == y.Product1 && x.Product2 == y.Product2;
}
public int GetHashCode(Sales obj)
{
return obj.Product1 + 65537 * obj.Product2;
}
}
The GetHashCode function is critical to Linq's performance with an intersection query - most elements of S1 and S2 will not even need to be compared with each other because they return different hash codes.
In the worst case (e.g. having a GetHashCode(...) function that simply does return 0
) it would end up comparing every item in S1 to every item in S2, the same as your current code.
Upvotes: 0
Reputation: 259
My suggestion is
S1.Where(x => S2.Any(y => x.Product1 == y.Product1 && x.Product2 == y.Product2))
.Select(x => x.ISSold = true);
.ToList();
Upvotes: 2
Reputation: 9519
Not sure what was wrong with foreach
, one way you could do this is:
var result = S1.Select(x =>
{
if (S2.Any(s2 => s2.Product1 == x.Product1 && s2.Product2 == x.Product2))
{
x.ISSold = true;
}
return x;
});
Upvotes: 1