English to PigLatin Conversion of a Sentence

Refresh

March 2019

Views

4.4k time

1

I am making a program to convert English to PigLatin. However, my solution only seems to work with one word. If I enter in more than ond word, only the last is translated.

testing one translation

Would simply output:

translationway

I've looked at some solutions, but most are in the same fashion as mine, or use "simplified" solutions beyond the scope of my knowledge.

Code:

 static void Main(string[] args)
    {
        Console.WriteLine("Enter a sentence to convert to PigLatin:");
        string sentence = Console.ReadLine();
        string pigLatin = ToPigLatin(sentence);
        Console.WriteLine(pigLatin);
    }

    static string ToPigLatin (string sentence)
    {            
        string firstLetter,
               restOfWord,
               vowels = "AEIOUaeio";
        int currentLetter;

        foreach (string word in sentence.Split())
        {
            firstLetter = sentence.Substring(0, 1);
            restOfWord = sentence.Substring(1, sentence.Length - 1);
            currentLetter = vowels.IndexOf(firstLetter);

            if (currentLetter == -1)
            {
                sentence = restOfWord + firstLetter + "ay";
            }
            else
            {
                sentence = word + "way";
            }
        }
        return sentence;

All help is greatly appreciated!


Edit

Thanks to great feedback, I've updated my code:

    static string ToPigLatin (string sentence)
    {

        const string vowels = "AEIOUaeio";
        List<string> pigWords = new List<string>();

        foreach (string word in sentence.Split(' '))
        {
            string firstLetter = word.Substring(0, 1);
            string restOfWord = word.Substring(1, word.Length - 1);
            int currentLetter = vowels.IndexOf(firstLetter);

            if (currentLetter == -1)
            {
                pigWords.Add(restOfWord + firstLetter + "ay");
            }
            else
            {
                pigWords.Add(word + "way");
            }
        }
        return string.Join(" ", pigWords);
    }

Would it be very complex to adapt this code to work with consonant clusters?

For example, right now testing one translation prints as:

estingtay oneway ranslationtay

While, as I understand PigLatin rules, it should read:

estingtay oneway anslationtray

3 answers

2

Just place += instead of = here:

if (currentLetter == -1)
{
    sentence += restOfWord + firstLetter + "ay";
}
else
{
    sentence += word + "way";
}

On your version, you were overriding the sentence in each iteration of your loop


Edit

I've made a lot of changes to the code:

public static string ToPigLatin(string sentence)
{
    const string vowels = "AEIOUaeio";
    List<string> newWords = new List<string>();

    foreach (string word in sentence.Split(' '))
    {
        string firstLetter = word.Substring(0, 1);
        string restOfWord = word.Substring(1, word.Length - 1);
        int currentLetter = vowels.IndexOf(firstLetter);

        if (currentLetter == -1)
        {
            newWords.Add(restOfWord + firstLetter + "ay");
        }
        else
        {
            newWords.Add(word + "way");
        }
    }
    return string.Join(" ", newWords);
}

As Panagiotis-Kanavos said, and he's damn right, don't build your output on your input but with your input. Thus, I added the newWords list (some might prefer a StringBuilder, I don't).

You were misusing your variable in your loop, especially with the Substrings calls, it's now fixed.

If you have any question on this, don't hesitate.

0

I came up with a short LINQ implementation for this:

string.Join(" ", "testing one translation".Split(' ')
    .Select(word => "aeiouy".Contains(word[0])
            ? word.Skip(1).Concat(word.Take(1))
            : word.ToCharArray())
    .Select(word => word.Concat("way".ToCharArray()))
    .Select(word => string.Concat(word)));

Output: "testingway neoway translationway"

Of course, I'd probably refactor it to something like this:

"testing one translation"
    .Split(' ')
    .Select(word => word.ToCharsWithStartingVowelLast())
    .Select(word => word.WithEnding("way"))
    .Select(word => string.Concat(word))
    .Join(' ');

static class Extensions {
    public static IEnumerable<char> ToCharsWithStartingVowelLast(this string word)
    {
        return "aeiouy".Contains(word[0])
            ? word.Skip(1).Concat(word.Take(1))
            : word.ToCharArray();
    }
    public static IEnumerable<char> WithEnding(this IEnumerable<char> word, string ending)
    {
        return word.Concat(ending.ToCharArray())
    }
    public static string Join(this IEnumerable<IEnumerable<char>> words, char separator)
    {
        return string.Join(separator, words.Select(word => string.Concat(word)));
    }
}

Update:

With your edit, you asked about consonant clusters. One of the things I like about doing this with LINQ is that it's pretty simple to just update that part of the pipeline, and make it all work:

 public static IEnumerable<char> ToCharsWithStartingConsonantsLast(this string word)
{
    return word.SkipWhile(c => c.IsConsonant()).Concat(word.TakeWhile(c => c.IsConsonant()));
}

public static bool IsConsonant(this char c)
{
    return !"aeiouy".Contains(c);
}

The entire pipeline, without refactoring to extension methods, now looks like this:

string.Join(" ", "testing one translation".Split(' ')
    .Select(word => word.SkipWhile(c => !"aeiouy".Contains(c)).Concat(word.TakeWhile(c => !"aeiou".Contains(c))))
    .Select(word => word.Concat("way".ToCharArray()))
    .Select(word => string.Concat(word)))

and outputs "estingtway oneway anslationtrway".

Update 2:

I noticed I wasn't handling word endings correctly. Here's an update that takes care of only adding w to the ending when the word (without the ending) ends with a vowel:

string.Join(" ", "testing one translation".Split(' ')
    .Select(word => word.SkipWhile(c => !"aeiouy".Contains(c)).Concat(word.TakeWhile(c => !"aeiou".Contains(c))))
    .Select(word =>
    {
        var ending = "aeiouy".Contains(word.Last()) ? "way" : "ay";
        return word.Concat(ending.ToCharArray());
    })
    .Select(word => string.Concat(word)))

Output: "estingtay oneway anslationtray". Note how it's only the step that handles adding the ending that changed - all other parts of the algorithm were unchanged.

Given how simple this now is, I'd probably only use two extension methods: Join(this IEnumerable<IEnumerable<char>> words, char separator) and IsConsonant(this char c) (the implementation of the latter should be easy given the code samples above). This yields the following final implementation:

"testing one translation"
        .Split(' ')
        .Select(word => word.SkipWhile(c => !c.IsVowel()).Concat(word.TakeWhile(c => c.IsVowel())))
        .Select(word => word.Concat((word.Last().IsVowel() ? "way" : "ay").ToCharArray()))
        .Select(word => string.Concat(word))
        .Join(" ")

It's also really easy to see here what we do to translate:

  1. Split the sentence into words
  2. Shuffle any consonants to the end of the word (it's admittedly not apparent at first sight that this is what happens, but I can't find a simpler way to express it except by wrapping it in an extension method)
  3. Add the ending
  4. Convert IEnumerable<char>s to strings
  5. Re-join the words into a sentence
0
 private static void Main(string[] args)
        {
            Console.WriteLine("Enter a sentence to convert to PigLatin:");
            string sentence = Console.ReadLine();
            var pigLatin = GetSentenceInPigLatin(sentence);
            Console.WriteLine(pigLatin);
            Console.ReadLine();
        }

        private static string GetSentenceInPigLatin(string sentence)
        {
            const string vowels = "AEIOUaeio";
            var returnSentence = "";
            foreach (var word in sentence.Split())
            {
                var firstLetter = word.Substring(0, 1);
                var restOfWord = word.Substring(1, word.Length - 1);
                var currentLetter = vowels.IndexOf(firstLetter, StringComparison.Ordinal);

                if (currentLetter == -1)
                {
                    returnSentence += restOfWord + firstLetter + "ay ";
                }
                else
                {
                    returnSentence += word + "way ";
                }
            }
            return returnSentence;
        }