Justin L
Justin L

Reputation: 15

Sorting a list of names with first/last name and age C#

I want to sort the names on this list first by age (which I have done so far), but I was wondering how I could then sort these names again by last name before printing to a new file. For example if I have 5 people that are 20 years old with different last names, how could I make sure those 5 people are in ascending alphabetical order?

class Person : IComparable
{
    string vorname;
    string nachname;
    int age;

    public Person(string vorname, string nachname, int age)
    {
        this.age = age;
        this.nachname = nachname;
        this.vorname = vorname;
    }

    public int CompareTo(object obj)
    {
        Person other = (Person)obj;
        int a = this.age - other.age;

        if (a != 0)
        {
            return -a;
        }
        else
        {
            return age.CompareTo(other.age);
        }
    }

    public override string ToString()
    {
        return vorname + " " + nachname + "\t" + age;
    }        
}

class Program
{
    static void Main(string[] args)
    {
        Person[] peeps = new Person[20];

        try
        {
            StreamReader sr = new StreamReader("inputNames.txt");

            int count = 0;

            while (!sr.EndOfStream)
            {
                string data = sr.ReadLine();
                Console.WriteLine();
                string[] info = data.Split(',');
                peeps[count] = new Person(info[0], info[1], int.Parse(info[2]));

                count++;
            }
            Array.Sort(peeps);
            sr.Close();
        }
        catch(FileNotFoundException e)
        {
            Console.WriteLine(e.Message);
        }

        StreamWriter sw = new StreamWriter("outputNames.txt");

        Console.WriteLine();
        foreach (Person p in peeps)
        {
            Console.WriteLine(p);
            sw.WriteLine(p);
        }
        sw.Close();
    }
}

Upvotes: 0

Views: 9129

Answers (5)

dragonfly02
dragonfly02

Reputation: 3669

This is how I would implement such a person class with some comments.

//sort a person object by age first, then by last name
    class Person : IComparable<Person>, IComparable
    {
        public string LastName { get; }
        public string FirstName { get; }
        public int Age { get; }

        public Person(string vorname, string nachname, int age)
        {
            LastName = vorname;
            FirstName = nachname;
            Age = age;
        }

        // used by the default comparer
        public int CompareTo(Person p)
        {
            // make sure comparable being consistent with equality; this will use IEquatable<Person> if implemented on Person hence better than static Equals from object
            if (EqualityComparer<Person>.Default.Equals(this, p)) return 0;

            if (p == null)
                throw new ArgumentNullException(nameof(p), "Cannot compare person with null");

            if (Age.CompareTo(p.Age) == 0)
            {
                return LastName.CompareTo(p.LastName);
            }
            return Age.CompareTo(p.Age);
        }

        // explicit implementation for backward compatiability 
        int IComparable.CompareTo(object obj)
        {
            Person p = obj as Person;
            return CompareTo(p);
        }

        public override string ToString() => $"{LastName} {FirstName} \t {Age}";
    }

Upvotes: 0

Konstantin Chernov
Konstantin Chernov

Reputation: 1936

Little bit old-fashioned, but still. You can use Comparers and use them later as you wish for flexibility:

    public class AgeComparer: Comparer<Person> 
    {
        public override int Compare(Person x, Person y)
        {   

           return x.Age.CompareTo(y.Age);      
        }    
    }
    public class LastNameThenAgeComparer: Comparer<Person> 
    {
        public override int Compare(Person x, Person y)
        {       
            if (x.LastName.CompareTo(y.LastName) != 0)
            {
               return x.LastName.CompareTo(y.LastName);
            }
            else (x.Age.CompareTo(y.Age) != 0)
            {
               return x.Age.CompareTo(y.Age);
            }    
        }    
    }
//// other types of comparers

Usage:

personList.Sort(new LastNameThenAgeComparer());

Upvotes: 1

Matthew Whited
Matthew Whited

Reputation: 22443

LINQ + Extension Methods

class Program
{
    static void Main(string[] args)
    {
        try
        {
            "inputNames.txt".ReadFileAsLines()
                            .Select(l => l.Split(','))
                            .Select(l => new Person
                            {
                                vorname = l[0],
                                nachname = l[1],
                                age = int.Parse(l[2]),
                            })
                            .OrderBy(p => p.age).ThenBy(p => p.nachname)
                            .WriteAsLinesTo("outputNames.txt");
        }
        catch (Exception e)
        {
            Console.Error.WriteLine(e.Message);
        }
    }
}
public class Person
{
    public string vorname { get; set; }
    public string nachname { get; set; }
    public int age { get; set; }

    public override string ToString()
    {
        return string.Format("{0} {1}\t{2}", this.vorname, this.nachname, this.age);
    }
}
public static class ToolsEx
{
    public static IEnumerable<string> ReadFileAsLines(this string filename)
    {
        using (var reader = new StreamReader(filename))
            while (!reader.EndOfStream)
                yield return reader.ReadLine();
    }
    public static void WriteAsLinesTo(this IEnumerable lines, string filename)
    {
        using (var writer = new StreamWriter(filename))
            foreach (var line in lines)
                writer.WriteLine(line);
    }
}

Upvotes: 0

SledgeHammer
SledgeHammer

Reputation: 7705

Linq is your friend. You can re-write all that code in 1 line:

peeps.OrderBy(x => x.Age).ThenBy(x => x.LastName);

That's all there is to it :). You can get rid of all that IComparable junk, that's old school.

EDIT: for IComparable, you can do:

    public int CompareTo(object obj)
    {
        Person other = (Person)obj;

        if (age < other.age)
            return -1;

        if (String.Compare(vorname, other.vorname) < 0)
            return -1;

        return 1;
    }

Seems to work for my quick testing, but test it more :).

Upvotes: 4

AJ X.
AJ X.

Reputation: 2770

You can use Linq:

 people.OrderBy(person => person.age)
     .ThenBy(person => person.LastName);

Upvotes: 1

Related Questions