0

Net core application. I have below query in my application

 var result  = sourceProposal.Quotes
      .Where(x=>x.QuotationId == sourceQuoteId)
      .FirstOrDefault()
      .QuoteLines.Select(x=>(x.Quantity,x.WtgType)).ToArray();

This yields in two array elements such as

0 element   1, "string1"
1 element   2, "string2"

What I am expecting is

(int[] sourceQuantity, string[] destinationTurbineType) = sourceProposal.Quotes
           .Where(x=>x.QuotationId == sourceQuoteId)
           .FirstOrDefault()
           .QuoteLines.Select(x=>(x.Quantity,x.WtgType)).ToArray();

I want to copy to tuple which has int[] sourceQuantity, string[] destinationTurbineType this piece of code is not working and throwing error does not contain definition for destructor and no accessible extension method Descontruct accepting first argument of type int(sourceQuantity, string destinationTurbineType)[].

Can someone help me to copy values to sourceQuantity and destinationTurbineType. Any help would be appreciated. Thanks

Prasad Telkikar
  • 15,207
  • 5
  • 21
  • 44
Mr Perfect
  • 585
  • 8
  • 30

2 Answers2

3

Select<TSource,TResult> returns enumerable/queryable of the type returned by selector (IEnumerabe<TResult>/IQueryable <TResult>).

If you want to achieve this with LINQ you can use Aggregate:

// note that sourceQuantity and destinationTurbineType would be lists, not arrays
var (sourceQuantity, destinationTurbineType) = sourceProposal.Quotes
    .Where(x=>x.QuotationId == sourceQuoteId)
    .FirstOrDefault()
    .QuoteLines
    .Aggregate((ints: new List<int>(), strs: new List<string>()), (aggr, curr) =>
    {
        aggr.ints.Add(curr.Quantity);
        aggr.strs.Add(curr.WtgType);
        return aggr;
    });

Or just use simple for loop and copy data to destination arrays (possibly move to some extension method). Something along this lines:

var quoteLines = sourceProposal.Quotes
    .Where(x=>x.QuotationId == sourceQuoteId)
    .FirstOrDefault()
    .QuoteLines; // assuming it is materialized collection with indexer like an array or list
int[] sourceQuantity = new int[quoteLines.Length]; // or Count
string[] destinationTurbineType = new string[quoteLines.Count()];
for(int i = 0; i < quoteLines.Length; i++)
{
   var curr = quoteLines[i];
   sourceQuantity[i] = curr.Quantity;
   destinationTurbineType[i] = curr.WtgType;
}
Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • Could you shorten this by removing the Where and doing the match on QuotationId in the FirstOrDefault? – sr28 May 06 '22 at 11:41
  • 1
    @sr28 yes. The original code has also a problem with `Default` part. – Guru Stron May 06 '22 at 11:44
  • 1
    Hi Thanks for your answer, It is working, May I know if there is any issue with original code – Mr Perfect May 06 '22 at 11:45
  • @MrPerfect was glad to help! `FirstOrDefault` will return `null` if the type is reference type and nothing was matched, so `.FirstOrDefault().QuoteLines` will throw NRE. – Guru Stron May 06 '22 at 11:48
  • Yes got it how we can avid it sir? – Mr Perfect May 06 '22 at 11:53
  • @MrPerfect using null checks ([several options here](https://stackoverflow.com/q/6417902/2501279)) and conditional logic. Or using null [conditional operators](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#null-conditional-operators--and-)(note that with value types it can behave in not very obvious way). – Guru Stron May 06 '22 at 12:02
  • 1
    `resultSelector: acc => (acc.ints.ToArray(), acc.strs.ToArray()` will convert the lists to arrays – Peter Csala May 06 '22 at 12:07
2

Currently there is no built-in LINQ method to do this. But you could write your own extension method. Something like the following:

public static class EnumerableExtensions
{
    public static (TFirst[] xs, TSecond[] ys) Unzip<TFirst, TSecond>(this IEnumerable<(TFirst, TSecond)> zipped)
    {
        var xs = new List<TFirst>();
        var ys = new List<TSecond>();
        foreach (var (x, y) in zipped)
        {
            xs.Add(x);
            ys.Add(y);
        }
        return (xs.ToArray(), ys.ToArray());
    }
}

var (xs, ys) =
    new[] { 1, 2, 3 }
    .Zip(new[] { "a", "b", "c" })
    .Unzip();

Console.WriteLine(string.Join(", ", xs)); // 1, 2, 3
Console.WriteLine(string.Join(", ", ys)); // a, b, c

Or in the case of your example, you could then use:

(int[] sourceQuantity, string[] destinationTurbineType) = sourceProposal.Quotes
           .Where(x=>x.QuotationId == sourceQuoteId)
           .FirstOrDefault()
           .QuoteLines.Select(x=>(x.Quantity,x.WtgType)).Unzip();
Clinton
  • 2,787
  • 1
  • 18
  • 10