Returning value from out modifier to a collection in C#

Viewed 187

Let's suppose I receive a collection of strings from user. I need to convert them to GUID sequences for further processing. There is a chance, that user may enter invalid data (not correct GUID sequence), so I need to validate input. Additionally, I can run business-process if only all uploaded data are correct GUID values. Here is my code:

IEnumerable<string> userUploadedValues = /* some logic */;
bool canParseUserInputToGuid = userUploadedValues.All(p => Guid.TryParse(p, out var x));
if(canParseUserInputToGuid)
    var parsedUserInput = userUploadedValues.Select(p=> Guid.Parse(p));

This logic works pretty well, but I don't like it as actually I am doing work twice. In second line, Guid.TryParse(p, out var x) already writing parsed GUID sequence to the X variable. Is there an approach to combine validating and mapping logic - if sequence elements satisfy for condition (All) then map this elements to a new collection (Select) in one query? It is important for me also in terms of performance, as it is possible that client will send large amount of data (1, 000, 000+ elements) and doing twice work here is a bit inefficient.

4 Answers

You can do something like this in one Select:

var parsedUserInput = userUploadedValues.Select(p => Guid.TryParse(p, out var x) ? x : default)
                                        .Where(p => p != default);

For this one, you need to be sure if there is no Guid.Empty input from the user.

Otherwise, you can return a nullable Guid if parsing doesn't succeed:

var parsedUserInput = userUploadedValues.Select(p => Guid.TryParse(p, out var x) ? x : default(Guid?))
                                        .Where(p => p != null);

Another solution by creating an extension method, for example:

public static class MyExtensions
{
    public static Guid? ToGuid(this string arg)
    {
        Guid? result = null;
        if(Guid.TryParse(arg, out Guid guid))
            result = guid;
        return result;
    }
}

and usage:

var parsedUserInput2 = userUploadedValues.Select(p => p.ToGuid())
                                         .Where(p => p != null);

But keep in mind that in this cases, you will have a collection of nullable Guids.

Your out var x variable will be Guid.Empty in the case where it is not a valid Guid. So you can just do this:

    IEnumerable<string> userUploadedValues = new[]
    {
        "guids.."
    };
    var maybeGuids = userUploadedValues.Select( x => {
                                                    Guid.TryParse( x, out var @guid );
                                                    return @guid;
                                                } );
    if ( maybeGuids.All( x => x != Guid.Empty ) )
    {
        //all the maybe guids are guids
    }

You can optimize the validation and conversion like below,

IEnumerable<string> userUploadedValues = /* some logic */;
var parsedGuids = userUploadedValues.Where(p => Guid.TryParse(p, out var x));
if(userUploadedValues.Count() != parsedGuids.Count())
{
    //Some conversion failed, 
}

If the count of both the lists same, then you have all the converted GUIDs in the parsedGuids.

Sometimes the non-LINQ method is just easier to read and no longer.

var parsedUserInput = new List<string>();
foreach(var value in userUploadedValues)
{
    if (Guid.TryParse(value, out var x)) parsedUserInput.Add(x);
    else...
}
Related