3

In C# 7 we have value tuples of the form (a, b, c, ...) and deconstruction of them (via var (a, b, c, ...) = (a, b, c, ...)).

I have a tuple of the form (row: <int>, column: <int>), and I want to create a new tuple of the form (row: <int>, column: <int>, content: <char>) using the previous one.

In Python I can do:

existing = [1, 2]
new = [*existing, 3]

Or with dicts:

existing = {'row':1, 'column':2}
new = {**existing, 'content':'1'}

Is there a similar pattern in C#?

I can of course deconstruct the tuple into variables, then recombine them:

var (row, column) = (row: 1, column: 2);
var result = (row, column, content: '1');

However the actual situation is more complex. I have a function that returns the existing tuple, and an one-line function that constructs the new. If I simplify this it will be like:

private (int row, int column) ExistingTuple() => (row: 1, column: 2);

private (int row, int column, char content) NewTuple() => (/* What should I do here */ExistingTuple(), content: '1');

For the simplicity and the readability, I want it as an arrow function (T F() => E;). If I would implement my suggestion, it will be seem like:

private (int row, int column, char content) NewTuple()
{
    var (row, column) = ExistingTuple();
    return (row, column, content: '1');
}

Is there a more elegant way to do that?

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77

2 Answers2

4

You can write it as an extension method, though it sounds like maybe that's not as concise as you'd like:

public static (T1, T2, T3) TupleAppend<T1, T2, T3>(
   this (T1, T2) me,
   T3 value
) =>
   (me.Item1, me.Item2, value);

Used as:

var triple = (1, 2).TupleAppend(3);

You'd also need an overload for every size, and even more overloads if you wanted to add more than one new item at once.

Dave Cousineau
  • 12,154
  • 8
  • 64
  • 80
  • If this is the only way I prefer to use my way. It's more readable, and less code to write. – Chayim Friedman Dec 08 '19 at 07:46
  • @ChayimFriedman As long as you put the extension methods in a library, and in a place by themselves so they aren't cluttering anything, you should just have to write them once. The actual appending operation is a lot clearer like this, compared to manual destructuring/restructuring. You can also use a shorter name than `TupleAppend` as well. (Could even be really short, like `a` or something, and then it's almost like an operator.) – Dave Cousineau Dec 08 '19 at 07:52
1

From a design perspective, these are probably two different data types. The (row, col) tuples are indexes for the actual data. You can therefore use nested tuples to express this intent:

private ((int row, int column), char content) EnrichedTuple() => 
    (ExistingTuple(), content: '1');
Tomer
  • 1,606
  • 12
  • 18