Month: December 2017

Puzzle: Singer Name to Group Type

Posted on

The Challenge

This challenge comes from listener Patrick Berry of Jasper, Ala. Name a famous singer — 3 letters in the first name, 5 letters in the last. Drop the middle letter of the last name and rearrange the result to name a variety of singing group. What is it?

The Solution

Dec31_2017_Solution
Screen-Shot Showing the Puzzle Solution

Synopsis

Fun, easy! I was fortunate to have a file lying around with singer names, and it was easy enough to go to Wikipedia to grab a list of musical group types.

Also: woo-woo! I Almost solved it without a single bug! I was able to fix my solitary bug during a debug session, so I can honestly say it ran the first time.

Techniques Used

The Code

string singerFile = @"Singers.txt";
string groupTypeFile = "MusicalGroupTypeList.txt";

//Make a function to sort the letters in a string, converting 
//to lower case, and removing all but letters
Func<string, string> sortLetters = (g) => g
                            .Where(c => char.IsLetter(c))
                            .Select(c => char.ToLower(c))
                            .OrderBy(c => c)
                            .Aggregate("", (p, c) => p + c);

//The dictionary key is the sorted letters; the 
//value is the original string
var groupTypeDic = new Dictionary<string, string>();

//Open the file of group types; use it to build the dictionary
using (StreamReader sr = File.OpenText(groupTypeFile)) {
    while (sr.Peek() != -1) {
        string grpType = sr.ReadLine();

        string key = sortLetters(grpType);
        if (!groupTypeDic.ContainsKey(key)) {
            groupTypeDic.Add(key, grpType);
        }
    }
}

//Now read all the singers from the file
using (StreamReader sr = File.OpenText(singerFile)) {
    while (sr.Peek() != -1) {
        string singerName = sr.ReadLine();
        string[] tokens = singerName.Split(' ');
        int lastIndex = tokens.Length - 1;
        if (tokens.Length > 1 
            && tokens[0].Length == 3 
            && tokens[lastIndex].Length == 5) {
            //Combine the first name and /1st 2 letters of last name, 
            //plus the last 2 letters of last name
            string remove3rdLetterLastName = tokens[0]
                                       + tokens[lastIndex].Substring(0, 2)
                                       + tokens[lastIndex].Substring(3);
            string sorted = sortLetters(remove3rdLetterLastName);
            //Does the dictionary contain the sorted letters?
            if (groupTypeDic.ContainsKey(sorted)) {
               //Yipee! Found a solution
               txtSolution.Text = $"{singerName} → {groupTypeDic[sorted]}";
            }
        }
    }
}

 

Explanation

Remember, if two words are anagrams, then their sorted letters are the same.

  • “Bob Dylan” → “bobdyan” →”abbdnoy”
  • “Boy Band” → “boyband” → “abbdnoy”

Encapsulate that Functionality in an Anonymous Function named ‘sortLetters’

Func<string, string> sortLetters = (g) => g
      .Where(c => char.IsLetter(c))
      .Select(c => char.ToLower(c))
      .OrderBy(c => c)
      .Aggregate("", (p, c) => p + c);

Here I create a function variable named ‘sortLetters’ that has one input parameter (string) and returns another string.

Inside the function, the input parameter (‘g’) is treated like a list of char, because I invoke Linq methods on it.

  • The ‘where-clause’ removes any spaces or other funky chars
  • The ‘select-clause’ converts ‘BobDylan’ → ‘bobdylan’
  • The ‘OrderBy’ clause converts ‘bobdylan’ to ‘abbdnoy’
  • The ‘Aggregate’ clause converts the result back to a string

Read all the Group Types, Hold Each in Variable ‘grpType’

using (StreamReader sr = File.OpenText(groupTypeFile)) {
 while (sr.Peek() != -1) {
 string grpType = sr.ReadLine();

Store the Group Type in a Dictionary

string key = sortLetters(grpType); 
if (!groupTypeDic.ContainsKey(key)) { 
    groupTypeDic.Add(key, grpType); 
}
Dictionary
Screen-Shot from debug session helps visualize the dictionary of group types

Read the Singers File, Store Each in Variable ‘singerName’

using (StreamReader sr = File.OpenText(singerFile)) { 
    while (sr.Peek() != -1) { 
        string singerName = sr.ReadLine();

Split the Singer Name into Parts, Proceed if Lengths are 3/5

string[] tokens = singerName.Split(' '); 
int lastIndex = tokens.Length - 1; 
if (tokens.Length > 1 
    && tokens[0].Length == 3 
    && tokens[lastIndex].Length == 5) {
  • The ‘split’ function returns an array of string
Split Results
Screen-Shot from debugger session shows how ‘Ziggy Marley’ → an array of length = 2

Remove 3rd Letter From Singer’s Name, Sort the Letters

 string remove3rdLetterLastName = tokens[0] 
                                  + tokens[lastIndex].Substring(0, 2) 
                                  + tokens[lastIndex].Substring(3); 
string sorted = sortLetters(remove3rdLetterLastName);

Here we concatenate the first name with the first 2 letters of last name and then the last 2 letters of the last name. Re-use the anonymous function to sort the letters.

If the Sorted Letters Match a Dictionary Entry, Add the Result to the TextBox

if (groupTypeDic.ContainsKey(sorted)) { 
    //Yipee! Found a solution 
    txtSolution.Text = $"{singerName} → {groupTypeDic[sorted]}"; 
}

Get the Code

You can download my code here. Note that you will need to create a (free) dropbox account to access the link.

Puzzle: US City With Only 3 Letters

Posted on

The Sunday Puzzle Challenge:

The name of what well-known U.S. city, in 10 letters, contains only three different letters of the alphabet? Puzzle URL

Synopsis

Fun and Simple! You can get the census place name file from the US Census Bureau.

CityPuzzleSolution
Screen Shot Shows the Solution

Techniques Used

Code to Solve the Puzzle

private void btnSolve_Click(object sender, RoutedEventArgs e) {
  string cityFile = 
	@"C:\Users\User\Documents\Puzzles\Data\AmericanPlaces2k.txt";

  using(StreamReader sr = File.OpenText(cityFile)) {
    while(sr.Peek() != -1) {
      string aLine = sr.ReadLine();
      //File is fixed-width, city starts in column 9
      string cityName = aLine.Substring(9, 30)
			     .TrimEnd();

      //Place names end with town/city/CDP; remove it now:
      if (cityName.EndsWith(" town"))
        cityName = cityName.Substring(0, cityName.Length - 5);
      else if (cityName.EndsWith(" CDP"))
        cityName = cityName.Substring(0, cityName.Length - 4);
      else if (cityName.EndsWith(" city"))
        cityName = cityName.Substring(0, cityName.Length - 5);

      //Remove spaces and other non-letters:
      var letters = cityName.ToLower()
                  .Where(c => char.IsLetter(c));

      //Check if the city has 10 letters and a distinct count of 3
      if (letters.Count() == 10) {
        int letterCount = letters.Distinct().Count();
        if (letterCount == 3) {
          txtSolution.Text += $"{cityName}\n";
        }
      }
    }
  }
}

Any Tricky Parts?

When you use Linq  on a string variable, it treats your variable as an array of char. That is how I can, for example, execute this line of code:

var letters = cityName.ToLower().Where(c => char.IsLetter(c));

In the code above, ‘c‘, refers to a char in the city name. Note that the result above, ‘letters‘, has data type ‘IEnumerble<char>’. I.e., everything is a char when you use Linq on a string.

Download the Code

You can download the code from my DropBox account. To do so, you will need to create a free account. If you run the downloaded code, you will need to adjust the path to the city name file. I included that file in the download.