Home > Enterprise >  When is my linq query returning an IEnumerable<short> object instead of an int?
When is my linq query returning an IEnumerable<short> object instead of an int?

Time:10-17

I'm doing a project where I am trying to convert snake case strings into camel case, but when I run my code CS0029 is thrown. I have no idea why this is happening.

using System;
using System.Linq;

public class Kata
{
    public static string ToCamelCase(string str)
    {
        string[] strSplit = str.Split(new char[] { '-', '_' });
        // this query is throwing an error
        int firstWordIndex = from s in strSplit
                             where s != "-" || s != "_"
                             select Array.IndexOf(strSplit, s);
        string firstWord = strSplit[firstWordIndex];

        var camelCase = from s in strSplit
                        where Array.IndexOf(strSplit, s) != firstWordIndex
                        select s.Replace(s[0], Char.ToUpper(s[0]));

        return string.Concat(firstWord, string.Join("", camelCase));
    }
}

CodePudding user response:

You can't do this:

int firstWordIndex = from s in strSplit
                     where s != "-" || s != "_"
                     select Array.IndexOf(strSplit, s);

When you Select from an enumerable thing (e.g. an array like Split gives you), you get another enumerable thing even if you put a where clause that ensures the new enumerable thing will have only one element

var array = new[]{ "a","b","c"}

array.Where(x => x == "a").Select(..) //returns you an "array" with only one element

If you want just one thing out of an enumerable, look at something like First()

    int firstWordIndex = (
        from s in strSplit
        where s != "-" || s != "_"
        select Array.IndexOf(strSplit, s)
    )
    .First();

First() (and its buddies FirstOrDefault, Single[OrDefault], Last[OrDefault]) will return you just one T out of an IEnumerable<T>. They accept a lambda that you could use instead of a Where, too (it would look like Array.IndexOf(strSplit, strSplit.First(s => s != "-" || s != "_"))


Also want to point out that s != "-" || s != "_" is surely true for every string in the world, because "-" != "_" - as a human you might say "get every ball out of this box that is not red or blue" but that means not(red or blue), which is equivalent to (not red) AND (not blue).

"not red or not blue" is always true because a red ball isn't blue and a blue ball isn't red, and a green ball is not red also (C# doesn't bother checking if a green ball is blue :D )

Common trap; watch out for it


I would have dug deeper into fixing your code, but for what it's worth, if I was writing a camelcaser I probably wouldn't use LINQ at all because there's a great risk it could become a very convoluted, low performance affair; even in the code as you have it, you split, then you loop (LINQ does a lot of looping internally) to find any char that isn't X, then you loop again (Array IndexOf) to find the index of that.. It would be a slight optimization to switch to method syntax, as Select((element, index) => index) could be used to return the index of the char without looping again but..

I'd probably lower the whole thing, turn it into a char array, loop over it and if the previous char was Char.IsPunctuation/IsWhitespace and the current char is not, I'd Char.ToUpper it otherwise Char.ToLower it, then remove the spaces.. If they were long strings I'd use a stringbuilder and not insert spaces into it. See https://docs.microsoft.com/en-us/dotnet/api/system.char.ispunctuation?view=net-5.0

  •  Tags:  
  • c#
  • Related