'Is it possible for String.split() to return tuple?

I have string which I need to split() and assign it's value into two variables. Is it possible for split() to return tuple instead of string[] so I can do something like:

String myString = "hello=world"
value, id = myString.Split('=');

I'm looking for some elegant solution.



Solution 1:[1]

If you can use C# 7 - you can use tuple deconstruction. If type has static or extension method named Deconstruct with appropriate signature - this type can be deconstructed. So you can have extension method like this:

public static class Extensions {
    public static void Deconstruct<T>(this IList<T> list, out T first, out IList<T> rest) {

        first = list.Count > 0 ? list[0] : default(T); // or throw
        rest = list.Skip(1).ToList();
    }

    public static void Deconstruct<T>(this IList<T> list, out T first, out T second, out IList<T> rest) {
        first = list.Count > 0 ? list[0] : default(T); // or throw
        second = list.Count > 1 ? list[1] : default(T); // or throw
        rest = list.Skip(2).ToList();
    }
}

And then you can deconstruct string array (which implements IList<string>) with this syntax (you might need to add appropriate using so that extension method above is reachable):

var line = "a=b";
var (first, second, _) = line.Split('=');
Console.WriteLine(first); // "a"
Console.WriteLine(second); // "b"

or

var line = "a=b";
var (first, (second, _)) = line.Split('=');
Console.WriteLine(first); // "a"
Console.WriteLine(second); // "b"

Which is quite close to what you need.

With just first extension method above (which deconstructs to first element and the rest) you can deconstruct to arbitrary length:

var (first, (second, _)) = line.Split('=');
var (first, (second, (third,  _))) = line.Split('=');
var (first, rest) = line.Split('=');
// etc

Second extension method is needed only if you want a little bit more convenient syntax for deconstruction of first 2 values (var (first, second, rest) instead of var (first, (second, rest)))

Solution 2:[2]

In C# 8 switch expression can be used for that:

var myString = "hello=world";
var (value, id) = myString.Split('=') switch { var a => (a[0], a[1]) };

Switch expression also allows you to handle different array lengths:

var myString = "hello=world";
var (value, id) = myString.Split('=') switch
{
    var a when a.Length == 2 => (a[0], a[1]),
    _ => (null, null)
};

Update: with C# 11 list patterns feature array can also be deconstructed:

var (value, id) = myString.Split('=') switch
{
    [var v, var i] => (v, i),
    _ => default
};

Unfortunately as of today something like this is still only in proposal stage:

[var value, var id] = myString.Split('='); // Does not compile!
var [value, id] = myString.Split('=');     // Does not compile!

But with is expression one can do the following:

if(myString.Split('=') is [var value, var id])
{
    Console.WriteLine((value, id));
}

Solution 3:[3]

Using LinQ:

List<Tuple<string, string>> pairs = lines
    .Select(x => 
    {
        string[] s = x.Split('=');
        return Tuple.Create(s[0], s[1]);
    })
    .ToList();

Solution 4:[4]

In C# 7 you can use var out and an extension to declare and assign multiple variables from an array with a group of extension methods:

static class MyExtensions {
    static void Assign<T>(this IList<T> items, out T v) {
        v = items[0];
    }
    static void Assign<T>(this IList<T> items, out T v0, out T v1) {
        v0 = items[0];
        v1 = items[1];
    }
    static void Assign<T>(this IList<T> items, out T v0, out T v1, out T v2) {
        v0 = items[0];
        v1 = items[1];
        v2 = items[2];
    }
    static void Assign<T>(this IList<T> items, out T v0, out T v1, out T v2, out T v3) {
        v0 = items[0];
        v1 = items[1];
        v2 = items[2];
        v3 = items[3];
    }
    ... // And so on
}

This extension lets you decompose your split into individual variables, rather than a tuple:

Assign(myString.Split('='), var out value, var out id);

Solution 5:[5]

You can create tuple dynamically using reflection, That will give you a tuple for a variable number of elements.

Try below code snippet,

    static void Main(string[] args)
    {
        var tupple = GetTuple<string>("hello=world".Split('='));
    }

    public static object GetTuple<T>(params T[] values)
    {
        Type genericType = Type.GetType("System.Tuple`" + values.Length);
        Type[] typeArgs = values.Select(_ => typeof(T)).ToArray();
        Type specificType = genericType.MakeGenericType(typeArgs);
        object[] constructorArguments = values.Cast<object>().ToArray();
        return Activator.CreateInstance(specificType, constructorArguments);
    }

Solution 6:[6]

Here is what i end up with hope will help you :

static void Main()
    {
        var a = new List<string> {
           "ID = Value",
           "id=value",
           "id      =    value"
        };

        var values = new List<string>();
        var ids = new List<string>();
        for (int i = 0; i < a.Count; i++)
        {
            (string value, string id) = SplitText(a[i]);
            values.Add(value);
            ids.Add(id);
        }

        Console.WriteLine($"Values -> {string.Join(", ", values)}, ID's -> {string.Join(", ", ids)}");
    }

    private static (string, string) SplitText(string inputText)
    {
        var tokens = inputText.Split(new[] { ' ', '=' }, StringSplitOptions.RemoveEmptyEntries);
        return (tokens[0], tokens[1]);
    }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Evk
Solution 2
Solution 3
Solution 4 Sergey Kalinichenko
Solution 5
Solution 6 FilipYordanov