if ConvertAll throw an exception on one element, can i just skip this element and continue to the next element?
No. The exception will need to be handled somewhere. If you expect exceptions to happen in the converter (and this is OK for the application), you must have a try-catch within the converter (the following code sample will return null for failed conversions):
List<string> input = new List<string> { "1", "2", "three", "4" };
List<int?> converted = input.ConvertAll(s =>
{
int? result = null;
try
{
result = int.Parse(s);
}
catch (Exception) { }
return result;
});
(yes, I know I should have used int.TryParse, but that would not throw an exception...)
However, eating exceptions like that always gives the smell of a workaround, and is nothing I would like to have in my code.
If you need to skip the throwing element entirely ConvertAll will not result for you, however you can implement a helper method for "robust enumeration". Something like this:
public static void Main(string[] args)
{
var integers = new List<int>() { 1, 2, -5 };
Converter<int, string> converter = x =>
{
if (x < 0)
throw new NotSupportedException();
return x.ToString();
};
// This code would throw
//var result1 = integers.ConvertAll(converter).ToArray();
//Console.WriteLine(String.Join(Environment.NewLine, result1));
// This code ignores -5 element
var result2 = RobustEnumerating(integers, converter).ToArray();
Console.WriteLine(String.Join(Environment.NewLine, result2));
}
public static IEnumerable<K> RobustEnumerating<T, K>(IEnumerable<T> input,
Converter<T, K> converter)
{
List<K> results = new List<K>();
foreach (T item in input)
{
try
{
results.Add(converter(item));
}
catch { continue; }
}
return results;
}
I would only do this if returning null or other unique value for failed conversions and then filtering the result to exclude those values is not applicable.
It may be simplest to combine, reverse, and shorten the answers given by Fredrik and Samir. Apply a Where first, then ConvertAll:
List<UnconvertedType> unconverted;
// do something to populate unconverted
List<ConvertedType> converted = unconverted.
Where(ut => ut != null).
ToList().
ConvertAll<ConvertedType>(ut => ut as ConvertedType);
This way you can remove any exceptions before they get considered and can remove the try/catch block.
Related
Suppose I have a container class that contains two properties:
string contents
bool isCommaSeperated
And a function:
? compute()
{
if(this.isCommaSeperated)
{
return contents.split(",").toList();
}
else
{
return contents;
}
}
Is there a way for this function to return either a string or a list of strings?
Or what type of design would allow me to achieve something similar?
I would just return both results as a collection:
IList<string> compute()
{
if (this.isCommaSeperated)
{
return contents.Split(","); // No need to turn the array into a list
}
return new[] { contents };
}
You can use dynamic to implement that:
dynamic Compute(bool isCommaSeperated)
{
if(isCommaSeperated)
{
return contents.Split(",").ToList();
}
else
{
return contents;
}
}
Code will still preserve type information, but let you return any type you like and fail at run-time if you try to use methods of another type.
Note that you give up compile type safety by doing it. Depending on your needs it may be ok, but consider is some alternative solution that preserve compile type safety would work better. I.e. returning single element array as shown in istme86's asnwer.
While IEnumerable<string> is the standard solution, you could also try the new StringValues struct which implicitly casts between string values and string arrays:
StringValues a = "A";
StringValues b = new string[] { "A", "B" };
StringValues c = null;
Console.WriteLine(a.Contains("A"));
// true
Console.WriteLine(b.Any(x => x == a));
// true
Console.WriteLine(c.Any());
// false
I have if statement like this:
if (myList.Any(x => s.Contains(x))
{
//some code here
}
in which I check if there is a string in myList which is contained in a string s.
Is it somehow possible to get the element x of this list and use it in the if statement showed above (in "//some code here" part), when the condition is met?
Thank you.
Switch from Any to FirstOrDefault, that will return the item that matched the test or null if no item was matched.
var found = myList.FirstOrDefault(x => s.Contains(x));
if (found != null)
{
//some code here
}
If null could be considered a "valid value" for a element in myList you can create a extension method TryFirst
public static class ExtensionMethods
{
public static bool TryFirst<T>(this IEnumerable<T> #this, Func<T, bool> predicate, out T result)
{
foreach (var item in #this)
{
if (predicate(item))
{
result = item;
return true;
}
}
result = default(T);
return false;
}
}
This would let you do
string foundString;
var wasFound = myList.TryFirst(x => s.Contains(x), out foundString);
if (wasFound)
{
//some code here
}
and tell the difference between a null in your list and a default result.
The above two methods only act on the first item on the list that the Contains will match, if you want to act on all the items use a Where( clause and a foreach
foreach(var item in myList.Where(x => s.Contains(x))
{
//some code here
}
You must promise you will not use the following code and use one of the other options first
You can also do your stated question, it is possible to get a variable assigned to inside lambada. However this can not be done with expression lambadas, only with statement lambadas.
string matachedString = null;
if (myList.Any(x => { var found = s.Contains(x);
if(found)
matachedString = x;
return found;
});
{
//some code here
}
But only do this option as a last resort, use one of the more appropriate methods like FirstOrDefaut or write a custom method like TryFirst first.
I'd use foreach/Where()for this, even if I'm only expecting 0 or 1 result:
foreach (var item in myList.Where(x => s.Contains(x)))
{
//some code here
}
I am trying to use SelectMany to compile a list of lists, however the selector may encounter an error and therefore have no results.
I'm going to provide the actual function I'm trying to write and then the snippet I wrote to further explore it.
In the production code, my goal is to handle the case that the serializer throws an exception.
In the sample code I have solved the problem for the case of strings, but am not sure if this solution is really more than a hack.
Production Code:
public List<click> Clicks(int advertiserId, DateTime date)
{
var dirInfo = new DirectoryInfo(DirName(date, advertiserId));
if (dirInfo.Exists)
{
var files = dirInfo.EnumerateFiles();
var clicks = files.SelectMany(c =>
{
using (var stream = c.OpenRead())
{
return (click[])clicksSerializer.Deserialize(stream);
}
});
return clicks.ToList();
}
return null;
}
Sample Code:
void Main()
{
var ids = new [] { 1, 2, 3 };
var names = ids.SelectMany(id =>
{
try
{
return GetNames(id);
}
catch
{
Console.WriteLine ("Error getting names for {0}", id);
// RESULT: NullReferenceException at System.Linq.Enumerable.<SelectManyIterator>d__14`2.MoveNext()
//return null;
// RESULT: an empty string in the result, but how to this with other types?
return new string [] { string.Empty };
}
});
foreach (var name in names)
{
Console.WriteLine ("Name: {0}", name);
}
}
string[] GetNames(int id)
{
switch (id)
{
case 1:
return new [] {"Jim", "Bob"};
case 2:
return new [] {"Harry", "Larry"};
default:
throw new Exception("invalid id");
}
}
Yes, returning an empty sequence is entirely valid, although I'd use
return Enumerable.Empty<string>();
instead to be clearer.
SelectMany - in the form you've used - really does just flatten a sequence of sequences, and if some of those intermediate sequences are empty, they'll just contribute nothing to the overall sequence. No problem in that at all.
You could return default(T) for some other type T although I'm not sure how useful that would be:
public static IEnumerable<T> TrySelectMany<T>(this IEnumerable<T> seq, Func<T, IEnumerable<T>> selector)
{
return seq.SelectMany(i => {
try {
return selector(i);
}
catch(Exception) {
return new T[] { default(T) };
}
});
}
I would consider returning Enumerable.Empty<T>() instead in the catch block.
On the client, I have an array of ints on which I call a ToString method; I'm then sending that string to the server via ajax.
On the server, I'm writing this:
var TestList = (from string s in TheString.Split(',')
select Convert.ToInt64(s)).ToList<long>();
Is this going to crash if the incoming string actually contains unexpected values?
Thanks.
If the string contains unexpected values it could throw a FormatException or an OverflowException as mentioned in the documentation for Convert.ToInt64(string).
To avoid the exception you could use bool long.TryParse(string, out long).
List<long> testList = new List<long>();
foreach (string s in theString.Split(','))
{
long number;
if (long.TryParse(s, out number))
{
testList.Add(number);
}
else
{
// Do something?
}
}
You could do it in a single statement, but you need the help of a helper method...
var TestList =
(from string s in TheString.Split(',')
let value = TryParseLong(s)
where value != null
select value.Value).ToList();
...
static long? TryParseLong(string s)
{
long result;
if (long.TryParse(s, out result))
return result;
return null;
}
Note: actually, you could do it without the helper method:
long value;
var TestList =
(from string s in TheString.Split(',')
where long.TryParse(s, out value)
select value).ToList();
But it's not a good idea, because the query produces side effects, which could cause unexpected behavior if the query becomes more complex.
I have a method that is supposed to check whether there is exactly one element in a collection that holds true for some predicate (given as a Func).
public bool ExistsUnique(Func<T, bool> p)
{
var tempCol = from i in MyCollection where p(i) select i;
return (tempCol.Count() == 1);
}
The problem with this is that when a second element that also holds true for the predicate
is found (for example two of the same string exists in a collection) the count is still 1. Which means it either overrides the first element or never adds the second because it already exists.
Any ideas as to how I can fix this method?
thx
/Peter
You can use the Single() method provided by LINQ like this:
public bool ExistsUnique(Func<T, bool> p)
{
try
{
var temp = myCollection.Single(x => p(x));
}
catch(Exception e)
{
// log exception
return false;
}
return true;
}
"Returns the only element of a sequence that satisfies a specified condition, and throws
an exception if more than one such element exists."
From http://msdn.microsoft.com/en-us/library/bb535118.aspx
EDIT
To avoid throwing an exception, you may also use the SingleOrDefault() method:
public bool ExistsUnique(Func<T, bool> p)
{
return myCollection.SingleOrDefault(x => p(x)) != null;
}
There must be some other problem. I'd suspect your predicate. For example, this returns a count of 2, as expected:
List<string> MyCollection = new List<string>()
{
"hello",
"hello"
};
var tempCol = from i in MyCollection where i == "hello" select i;
int count = tempCol.Count();
I doubt that it's the way you're calling it, either. The following works (returns false):
static List<string> MyCollection = new List<string>()
{
"hello",
"hello"
};
static bool ExistsUnique(Func<string, bool> p)
{
var tempCol = from i in MyCollection where p(i) select i;
return tempCol.Count() == 1;
}
static void DoIt()
{
bool isUnique = ExistsUnique((s) => s.Equals("hello"));
Console.WriteLine(isUnique);
}
Are you sure tempCol has looped completely through MyCollection?
is Count() a method that forces the complete loop or is it lazy?
Does for example tempCol.ToList().Count give the correct result?
This implementation would make it so you don't have to actually enumerate the entire collection, so will save you some execution time.
public bool ExistsUnique(Func<T, bool> p)
{
return MyCollection.Where(i => p(i)).Take(2).Count() == 1;
}
The Take(2) limits the Count to only enumerate the first two meeting the criteria.