1

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

    }
}

3 Answers 3

2

My suggestion is

        S1.Where(x => S2.Any(y => x.Product1 == y.Product1 && x.Product2 == y.Product2))
          .Select(x => x.ISSold = true);
          .ToList();
Sign up to request clarification or add additional context in comments.

4 Comments

the issue here is that your result will only contain the Sales which have ISSold set to true, i think OP wanted a list of all of them.
I think his intention is to set IsSold to true. So that's what happens in Select. I also added ToList to make Select actually executed.
I think it would be better if you used ForEach instead of select, just swap ToList and Select around then rename. This also seperates the query from the mutation.
It will iterate S1 collection two times instead of 1.
1

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

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.