5

I have a delimited string that I need sorted. First I need to check if 'Francais' is in the string, if so, it goes first, then 'Anglais' is next, if it exists. After that, everything else is alphabetical. Can anyone help me? Here's what I have so far, without the sorting

private string SortFrench(string langs)
    {
       string _frenchLangs = String.Empty;
       string retval = String.Empty;

        _frenchLangs = string.Join(" ; ",langs.Split(';').Select(s => s.Trim()).ToArray());

        if (_frenchLangs.Contains("Francais"))
            retval += "Francais";

        if (_frenchLangs.Contains("Anglais"))
        {
            if (retval.Length > 0)
                retval += " ; ";

            retval += "Anglais";
        }

        //sort the rest

        return retval;
    }
4
  • Hints: if and Contains(). What else you need? Commented May 15, 2013 at 14:08
  • sorry, yes, I can easily check for the first 2 values, but how do I sort the rest and omit the first values? Commented May 15, 2013 at 14:14
  • Wait, something isn't clear here. In your code you split a string delimited by ";" rebuilding a string delimited by ";"...What am I missing? Is the trim so important? Just curious :) Commented May 15, 2013 at 14:16
  • the only thing that's important is that the finished string is delimited, with spaces before and after each semicolon and that it's sorted as I described Commented May 15, 2013 at 14:18

9 Answers 9

11

Someone liked my comment, so figured I'd go ahead and convert that into your code:

private string SortFrench(string langs)
{
    var sorted          = langs.Split(';')
        .Select(s => s.Trim())
        .OrderByDescending( s => s == "Francais" )
        .ThenByDescending( s => s == "Anglais" )
        .ThenBy ( s => s )
        .ToArray();

    return string.Join(" ; ",sorted);
}

My syntax may be off slightly as I've been in the Unix world for awhile now and haven't used much LINQ lately, but hope it helps.

Sign up to request clarification or add additional context in comments.

2 Comments

I tried using this, even with s.Contains, but I get errors (Invalid expression term 'return')
Sorry, leftover from doing too much PHP closures...just remove the return, the return is implicit in C#. As I said...syntax may be off :(, sorry.
6

Here's what I came up with. You could change the .Sort() for a OrderBy(lang => lang) after the Select, but I find it's cleaner that way.

public string SortLanguages(string langs)
{
    List<string> languages = langs.Split(';').Select(s => s.Trim()).ToList();

    languages.Sort();
    PlaceAtFirstPositionIfExists(languages, "anglais");
    PlaceAtFirstPositionIfExists(languages, "francais");

    return string.Join(" ; ", languages);
}

private void PlaceAtFirstPositionIfExists(IList<string> languages, string language)
{
    if (languages.Contains(language))
    {
            languages.Remove(language);
            languages.Insert(0, language);
    }
}

Comments

6

You should use a custom comparer class

it will allow you to use the built in collection sorting functions, or the linq OrderBy using your own criteria

6 Comments

This is a little bit complex to accomplish a simple task, isn't it?
Why is this complex? My syntax is probably off for lack of use lately...but Linq is extremely easy: turn it into an Enumerable and array.OrderByDescending( s => return s == "Francais" ).ThenByDescending( s => return s == "Anglais" ).ThenBy( s => return s; );
@KevinNelson That's the best answer I've seen on here so far.
@KevinNelson I'm talking about the custom comparer class. Your approach is totally clean and simple (+1)
@FrancescoDeLisi [deleted original comment] - I see what you mean...sorry, I'm brain dead this morning...that custom comparer is a bit more complex...but probably faster as well. Linq is extremely easy, but has some performance gotchas.
|
3

Try this:

private string SortFrench(string langs)
{
    string _frenchLangs = String.Empty;

    List<string> languages = langs
        .Split(';')
        .Select(s => s.Trim())
        .OrderBy(s => s)
        .ToList();

    int insertAt = 0;

    if (languages.Contains("Francais"))
    {
        languages.Remove("Francais");
        languages.Insert(insertAt, "Francais");
        insertAt++;
    }

    if(languages.Contains("Anglais"))
    {
        languages.Remove("Anglais");
        languages.Insert(insertAt, "Anglais");
    }

    _frenchLangs = string.Join(" ; ", languages);

    return _frenchLangs;
}

Comments

2

All can be done in single line

private string SortFrench(string langs)
{
    return string.Join(" ; ", langs.Split(';').Select(s => s.Trim())
                    .OrderBy(x => x != "Francais")
                    .ThenBy(x => x != "Anglais")
                    .ThenBy(x=>x));
}

3 Comments

Given the string "foo;bar;Anglais;Francais;barby;fooby", that produces "Francais ; Anglais ; foo ; bar ; barby ; fooby", which is incorrect. The OP wanted the languages after the first two to be ordered alphabetically.
@JimMischel Good Point added final ThenBy :) Thanks for pointing out
I like this solution. It's short and understandable, and should be efficient.
0

Sorting alphabetically is simple; adding .OrderBy(s => s) before that .ToArray() will do that. Sorting based on the presence of keywords is trickier.

The quick and dirty way is to split into three:

  • Strings containing "Francais": .Where(s => s.Contains("Francais")
  • Strings containing "Anglais": .Where(s => s.Contains("Anglais")
  • The rest: .Where(s => !francaisList.Contains(s) && !anglaisList.Contains(s))

Then you can sort each of these alphabetically, and concatenate them.

Alternatively, you can implement IComparer using the logic you described:

For strings A and B:

  • If A contains "Francais"
    • If B contains "Francais", order alphabetically
  • Else
    • If B contains "Francais", B goes first
    • Else
      • If A contains "Anglais"
        • If B contains "Anglais", order alphabetically
        • Else, A goes first
      • Else, order alphabetically

There may be room for logical re-arrangement to simplify that. With all that logic wrapped up in a class that implements IComparer, you can specify that class for use by .OrderBy() to have it order your query results based on your custom logic.

5 Comments

I don't think the OP wants to search for values that have Francais or Anglais as a substring. Otherwise this is a good approach.
"First I need to check if 'Francais' is in the string, if so, it goes first, then 'Anglais' is next, if it exists" Saying "in the string" suggests substring. If "Francais" or "Anglais" is the entire string, then... well, this will work anyway :P
Oh, I forgot that it's coming from a single string. My mistake. Still, after the .Split() this will still work.
The OP is splitting a string on ; and I assume that "in the string" was referring to that initial string. And this would work for that case as long as there are not values in the list that have Francais or Anglais as substrings.
Indeed. As the strings seem to be the names of languages in French, I think that's a safe assumption. If not, swap the .Contains() for .Equals() and all is good.
0

You can also use Array.Sort(yourStringArray)

Comments

0

This code creates a list of the languages, sorts them using a custom comparer, and then puts the sorted list back together:

        const string langs = "foo;bar;Anglais;Francais;barby;fooby";
        var langsList = langs.Split(';').ToList();
        langsList.Sort((s1, s2) =>
            {
                if (s1 == s2)
                    return 0;
                if (s1 == "Francais")
                    return -1;
                if (s2 == "Francais")
                    return 1;
                if (s1 == "Anglais")
                    return -1;
                if (s2 == "Anglais")
                    return 1;
                return s1.CompareTo(s2);
            });
        var sortedList = string.Join(";", langsList);
        Console.WriteLine(sortedList);

Comments

0

This way you can set any list of words in front:

private static string SortFrench(string langs, string[] setStartList)
{
    string _frenchLangs = String.Empty;
    List<string> list = langs.Split(';').Select(s => s.Trim()).ToList();
    list.Sort();
    foreach (var item in setStartList){
    if (list.Contains(item))
    {
        list.Remove(setFirst);
    }
   }
    List<string> tempList = List<string>();
    tempList.AddRange(setStartList);
    tempList.AddRange(list);
    list = tempList;
    _frenchLangs = string.Join(" ; ", list);

    return _frenchLangs;
}

2 Comments

But he has to set a first and second. You'll need to change your code so it allows a setFirst and a setSecond.
Ok this way he can set any set of words in front, could be list but i started with array so just let it be.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.