Kyu96
Kyu96

Reputation: 1349

Match List<string> to List<object> using LINQ?

Assume we have an input list with the following values (all are strings):

var listA = new List<string>();
listA.Add("test");
listA.Add("123");
listA.Add("5.7");

and we are also given a second list:

var listB = new List<object>();
listB.Add(typeof(string));
listB.Add(typeof(int));
listB.Add(typeof(float));

I want to verify if all values in ListA are in the right format by matching it with the list of types in ListB. Both list will have the same length.

If yes I'd like to get a List as return value where all values of ListA are stored in the format as specified in ListB. If one converion would fail I would like to be able to throw a custom exception. Something like

throw new MyException($"Failed to convert value {valueX} to {type}");

I can only imagine a very ugly solution with for loops, lots of casts/conversions and copying. Is there an elegant solution to this?

Upvotes: 2

Views: 236

Answers (3)

TheGeneral
TheGeneral

Reputation: 81493

You could Zip the lists together, then use the Convert.ChangeType Method

Returns an object of a specified type whose value is equivalent to a specified object.

It will throw an exception of the following types

  • InvalidCastException This conversion is not supported. -or- value is null and conversionType is a value type. -or- value does not implement the IConvertible interface.

  • FormatException value is not in a format recognized by conversionType.

  • OverflowException value represents a number that is out of the range of conversionType.

  • ArgumentNullException conversionType is null.

Example

var listA = new List<string> { "test", "123", "5.7" };
var listB = new List<Type> { typeof(string), typeof(int), typeof(int) };
    
var combined = listA.Zip(listB, (s, type) => (Value :s, Type:type));

foreach (var item in combined)
{
   try
   {
      Convert.ChangeType(item.Value, item.Type);
   }
   catch (Exception ex)
   {
      throw new InvalidOperationException($"Failed to cast value {item.Value} to {item.Type}",ex);
   }
}

Full Demo Here

Side note: Technically speaking this is not casting per se, it's changing/converting the type

Upvotes: 1

Anu Viswan
Anu Viswan

Reputation: 18155

You could do the following with Zip.

var result = listA.Zip(listB,(value,type)=> 
                        { 
                           try{return Convert.ChangeType(value,(Type)type);} 
                           catch{throw new Exception($"Cannot cast between value {value} to Type {type}");}
                        });

By having the conversion within the Zip, would ensure you wouldn't have to convert the whole list if there is an exception earlier in the list.

Upvotes: 1

Alen.Toma
Alen.Toma

Reputation: 4870

Ok if you want to avoid forloop and do it with linq is still possible

Here is what i gott

var listA = new List<string>();
    listA.Add("test");
    listA.Add("123");
    listA.Add("5.7");

   var listB = new List<Type>();
    listB.Add(typeof(string));
    listB.Add(typeof(int));
    listB.Add(typeof(float));
   var index =-1;

try{
var newList = listA.Select((x, i)=>  Convert.ChangeType(x, listB[(index = i)])).ToList();

}catch(Exception e){

 throw  new Exception("Failed to cast value "+listA[index]+" to "+listB[index]);

}

Upvotes: 0

Related Questions