HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpModerate

Sorting a list of first names from a text file

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
sortingfiletextnamesfirstlistfrom

Problem

I applied for a job as a C#/.NET Junior Developer, and had a test to do::


Using names.txt (right click and 'Save Link/Target As...'), a 46K text
file containing over five-thousand first names, begin by sorting it
into alphabetical order. Then working out the alphabetical value for
each name, multiply this value by its alphabetical position in the
list to obtain a name score.


For example, when the list is sorted into alphabetical order, COLIN,
which is worth 3 + 15 + 12 + 9 + 14 = 53, is the 938th name in the
list. So, COLIN would obtain a score of 938 × 53 = 49714. What is
the total of all the name scores in the file?

I'm looking for some constructive comments on how I could have done it better.

```
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Name.Data.RetrieveNames
{
public class Names
{
public string Name { get; set; }
}

public static class RetrieveListOfNames
{
public static IEnumerable LstOfNames()
{
//****
//
//File location is stored in app.config file,
//this allows for ease in case the file needs
//to be moved somewhere else.
//So 1st check that it exists
//
//****
var fileExists = File.Exists(ConfigurationManager.AppSettings["LocationOfNamesFile"]);
//****
//
//If file does not exist, throw file not found exception, as no point in continuing
//
//****
if (!fileExists) throw new FileNotFoundException("FileNotFound Check AppSettings Location");
//****
//Set buffer size, on huge file this can help with pe

Solution


  • Use File.ReadLines to read from your file. This makes everything a lot simpler and avoids having to work with stream readers and buffers. You will just get back every line, so you don’t have too much about things.



  • Regex.Split(line, ",") – Don’t use Regex.Split here but just String.Split: line.Split(',')



  • List sumOffAllNames – This shouldn’t be a list of strings for two reasons: A sum is a single value, and more importantly, it is a number.



  • Don’t use Regex.Replace but just use String.Replace for simple static replacements.



  • Regex.Replace(…).Replace(…) actually uses two different replace methods: First it uses Regex.Replace, which returns a string, and then you use String.Replace.



  • For the character to number conversions, use a dictionary that just looks up the values for each character. Or use arithmetics to calculate the value based on the character value.



  • You shouldn’t need to use int.Parse for this situation ever. If instead of replacing characters by number strings you just collect the numbers directly, then you can just sum them later without having to parse them (you actually parse them multiple times making this worse).



Finally, you are looping over the list of names three times in total: Once over the file to collect the names; and then once over the list of names to create sumOfAllNames. Since you collect the sum for each name separately in that list, you need to again loop over that list (which has the same length as you have names) to get the actual sum.

You can actually do this all much easier. All you need to do is get the names, sort them, and then iterate the sorted names collection once and sum the values directly.

// read the lines from the file
File.ReadLines(fileName)
    // if there are multiple names per line, extract all of them
    .SelectMany(line => line.Split(','))

    // sort by the name
    .OrderBy(name => name)

    // now for each name, we also get the index and use that to calculate the score
    .Select((name, index) => {
        // convert to lower
        name = name.ToLower();

        // convert each character to an int, and subtract 96
        // (since the character 'a' has the value 97);
        // finally get the sum
        int score = name.AsEnumerable().Select(character => character - 96).Sum();

        // multiplicate by the index, add 1 because the index starts at 0
        score *= index + 1;

        // return the score
        return score;
    })
    // finally, calculate the sum of all scores
    .Sum();


For the future, if you need a more complicated lookup that does not work using character arithmetics, use a dictionary:

Dictionary scoreLookup = new Dictionary() {
    { 'a', 1 }, { 'b', 2 }, { 'c', 3 }, { 'd', 4 }, { 'e', 5 }, { 'f', 6 }, { 'g', 7 },
    { 'h', 8 }, { 'i', 9 }, { 'j', 10 }, { 'k', 11 }, { 'l', 12 }, { 'm', 13 }, { 'n', 14 },
    { 'o', 15 }, { 'p', 16 }, { 'q', 17 }, { 'r', 18 }, { 's', 19 }, { 't', 20 },
    { 'u', 21 }, { 'v', 22 }, { 'w', 23 }, { 'x', 24 }, { 'y', 25 }, { 'z', 26 } };

// then to convert the string
name.ToLower().Select((character, index) => scoreLookup[character] * (index + 1)).Sum();

Code Snippets

// read the lines from the file
File.ReadLines(fileName)
    // if there are multiple names per line, extract all of them
    .SelectMany(line => line.Split(','))

    // sort by the name
    .OrderBy(name => name)

    // now for each name, we also get the index and use that to calculate the score
    .Select((name, index) => {
        // convert to lower
        name = name.ToLower();

        // convert each character to an int, and subtract 96
        // (since the character 'a' has the value 97);
        // finally get the sum
        int score = name.AsEnumerable().Select(character => character - 96).Sum();

        // multiplicate by the index, add 1 because the index starts at 0
        score *= index + 1;

        // return the score
        return score;
    })
    // finally, calculate the sum of all scores
    .Sum();
Dictionary<char, int> scoreLookup = new Dictionary<char, int>() {
    { 'a', 1 }, { 'b', 2 }, { 'c', 3 }, { 'd', 4 }, { 'e', 5 }, { 'f', 6 }, { 'g', 7 },
    { 'h', 8 }, { 'i', 9 }, { 'j', 10 }, { 'k', 11 }, { 'l', 12 }, { 'm', 13 }, { 'n', 14 },
    { 'o', 15 }, { 'p', 16 }, { 'q', 17 }, { 'r', 18 }, { 's', 19 }, { 't', 20 },
    { 'u', 21 }, { 'v', 22 }, { 'w', 23 }, { 'x', 24 }, { 'y', 25 }, { 'z', 26 } };

// then to convert the string
name.ToLower().Select((character, index) => scoreLookup[character] * (index + 1)).Sum();

Context

StackExchange Code Review Q#98870, answer score: 10

Revisions (0)

No revisions yet.