How to get 'named' tuple components inside Linq queries?
Asked Answered
U

4

7

Suppose I have a of tuples, say List<(string, Table)> and I want to iterate over it using a Parallel.ForEach, using the 'named version' of the tuples' components.

The following code does that:

List<(string, Table)> list = new List<(string, Table)>();
Parallel.ForEach(list, tuple => 
{
    (string name, Table table) = tuple;

    // do stuff with components 'name' and 'table' here
});

I want to use name and table, instead of tuple.Item1 and tuple.Item2 respectively, since this makes code more readable. To make that work, I had to declare the tuple components inside the ForEach, if I wanted to use their 'named' versions.


MY QUESTION IS:

Is there a syntax in C# which allows us to get the deconstructed version of the tuple, avoiding that declaration inside the ForEach's body?

And if there is no such syntax, how could we achieve that with extension methods?


I mean, something like this:

List<(string, Table)> list = new List<(string, Table)>();
Parallel.ForEach(list, (string name, Table table) => 
{
    // do stuff with variables 'name' and 'table' here
});

Or maybe this?

List<(string, Table)> list = new List<(string, Table)>();
Parallel.ForEach(list, (name, table) => 
{
    // do stuff with variables 'name' and 'table' here
});


And, if there is a syntax for that, would it also apply to other Linq queries?

E.g.

string[] names = parsed.Select((string name, Table table) => name).ToArray();

Instead of:

string[] names = parsed.Select(t => t.Item1).ToArray();


This would be so nice to have, especially when working with tuples containing several components, e.g. List<(int, string, int, DateTime, ...)>. We would be able to give some context to the tuples' components inside complex Linq queries!

Unbelieving answered 24/9, 2018 at 10:53 Comment(2)
The question links to: github.com/dotnet/csharplang/issues/355 which is still open.Aquitaine
Not exactly duplicate. Yes, sadly this seems to be the cause of the issue... HOWEVER I'm also looking for workarounds like extension methods, please see my question in the middle of the post.Toby
G
6

How to get 'named' tuple components inside Linq queries?

The easiest solution would be to give your items a name, like so

List<(string Name, Table Table)> list = new List<(string, Table)>();
// or simply
var list = new List<(string Name, Table Table)>();

Parallel.ForEach(list, t => 
{
    // do something with t.Name, t.Table
    var name = t.Name;
    var table = t.Table;
});

Is there a syntax in C# which allows us to get the deconstructed version .. inside the ForEach

Afaik no.

Glassblowing answered 24/9, 2018 at 11:8 Comment(5)
Nice one. How could I miss that! ThanksToby
@Unbelieving how is this different from your first snippet?Geoffrey
@AlexandruClonțea it's myTuple.Item1 vs. myTuple.MyName.Dang
But... (string name, Table table) = tuple; decomposes the tuple. I thought the issue was about decomposition, which as far as I know is not supported in Expression Trees/LINQGeoffrey
@AlexandruClonțea - the problem is, that working with t.Item1 makes it difficult to understand what's inside. Decomposing the tuple is one option to solve this. So, it's "can we simplify decomposing?" - as you and I and others wrote: no. Is there another way to solve "the real problem"? Yes. Both is mentioned in the question and in the answer.Dang
B
2

It seems to me that you want something like this?

string[] names = parsed.Cast<(string name, Table table)>().Select(p => p.name).ToArray();
Broucek answered 29/3, 2019 at 6:47 Comment(3)
This also works, but at a cost: casting the collection. Just declaring parsed as a collection of (string name, Table table) was enough to solve the issue: the named parts of the tuple can now be accessed in LINQ queries.Toby
I mean a case when parsed does not have strong types. For example, parsed.Select(p => (p.i, p.j)).Cast...Broucek
Yeah, in case you cannot touch the declaration of parsed, then yes, it would be a possible solution. +1Toby
A
2

You have to use : instead of =

var k = executeResultSet.Select(s => new
{
    IDUser = (int) s["IDUser"],
    SourceCode = s["SourceCode"],
    SourceSystem = s["SourceSystem"]
}).ToList();

var k = executeResultSet.Select(s => 
(
    IDUser: (int) s["IDUser"],
    SourceCode: s["SourceCode"],
    SourceSystem: s["SourceSystem"]
)).ToList();
Avrilavrit answered 16/7, 2020 at 13:39 Comment(0)
S
0

Not exactly what you want, but this is an alternative to Christoph's answer:

List<(string, Table)> list = new List<(string, Table)>();
Parallel.ForEach(list, ((string name, Table table) t) => 
{
    // do stuff with variables 't.name' and 't.table' here
});
Subvene answered 3/1, 2022 at 14:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.