working with classes, methods, and sorting

JJohnson1701

New member
Joined
Sep 17, 2018
Messages
3
Programming Experience
1-3
Hi everyone,

I'm trying to help out a friend on some C# and I'm stumped here.

The issue is to show the proper sort for a series of classes representing a warehouse order. I found out the right order in Excel, but we need to show it program-wise.

The skeleton program is:

C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WarehouseOptimization
{
    class Program
    {
        static void Main(string[] args)
        {

            /*
             * WAREHOUSE OPTIMIZATION
             *
             * Please do not post your solution to this problem online (Git, etc.) Please keep the solution
             * in Dropbox folder we shared with you.
             * 
             * At ACME warehouse the warehouse manager has observed orders taking a long time to pick and
             *  believes that moving more popular items to easier to access locations may help.
             *
             * - Workers pick an order by placing items on a pallet at the dock door.
             * - Products are stored (inefficiently) in a single aisle with multiple shelves.
             * - As the shelf number increases, it is located further down the aisle and
             *   further away from the dock door.
             * - On average it takes 5*X seconds for a warehouse worker to retrieve an item
             *   from shelf X and place it on the pallet.
             * - A warehouse worker can only carry a single item at a time.
             * - A shelf can only hold one type of item.
             *
             * Which items should be on which shelves to optimize picking speed based on
             *  yesterday's orders?
             *
             *
             * Example output
             *
             * Shelf 1:      Item 12
             * Shelf 2:      Item 11
             * Shelf 3:      Item 10
             * Shelf 4:      Item 09
             * Shelf 5:      Item 08
             * Shelf 6:      Item 07
             * Shelf 7:      Item 06
             * Shelf 8:      Item 05
             * Shelf 9:      Item 04
             * Shelf 10:     Item 03
             * Shelf 11:     Item 02
             * Shelf 12:     Item 01
             *
             */

            OrderRepository orderRepo = new OrderRepository();

            // write your code here

            Console.WriteLine("Order " + Order[1].OrderLine[1]);

And the classes are here:

C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WarehouseOptimization
{
    public class Order
    {
        public OrderLine[] orderLines;
        public int id;
        public string customerName;
    }

    public class OrderLine
    {
        public int quantity;
        public string itemName;
    }

    public class OrderRepository
    {
        public Order[] GetYesterdaysOrders()
        {
            Order[] orders = new Order[] {
                                new Order{
                                    id = 1,
                                    orderLines = new OrderLine[] {
                                        new OrderLine{ itemName = "Item 01", quantity = 1},
                                        new OrderLine{ itemName = "Item 02", quantity = 3},
                                        new OrderLine{ itemName = "Item 03", quantity = 25},
                                        new OrderLine{ itemName = "Item 04", quantity = 12},
                                    },
                                },
                                new Order{
                                    id = 2,
                                    orderLines = new OrderLine[] {
                                        new OrderLine{ itemName = "Item 01", quantity = 1},
                                        new OrderLine{ itemName = "Item 08", quantity = 42},
                                        new OrderLine{ itemName = "Item 09", quantity = 13},
                                        new OrderLine{ itemName = "Item 12", quantity = 37},
                                    },
                                },
                                new Order{
                                    id = 3,
                                    orderLines = new OrderLine[] {
                                        new OrderLine{ itemName = "Item 12", quantity = 16},
                                    },
                                },
                                new Order{
                                    id = 4,
                                    orderLines = new OrderLine[] {
                                        new OrderLine{ itemName = "Item 10", quantity = 11},
                                        new OrderLine{ itemName = "Item 11", quantity = 10},
                                    },
                                },
                                new Order{
                                    id = 5,
                                    orderLines = new OrderLine[] {
                                        new OrderLine{ itemName = "Item 06", quantity = 7},
                                        new OrderLine{ itemName = "Item 07", quantity = 2},
                                        new OrderLine{ itemName = "Item 12", quantity = 14},
                                    },
                                },
                                new Order{
                                    id = 6,
                                    orderLines = new OrderLine[] {
                                        new OrderLine{ itemName = "Item 05", quantity = 17},
                                    },
                                },
                                new Order{
                                    id = 7,
                                    orderLines = new OrderLine[] {
                                        new OrderLine{ itemName = "Item 03", quantity = 5},
                                        new OrderLine{ itemName = "Item 07", quantity = 2},
                                    },
                                },
                                new Order{
                                    id = 8,
                                    orderLines = new OrderLine[] {
                                        new OrderLine{ itemName = "Item 02", quantity = 13},
                                        new OrderLine{ itemName = "Item 07", quantity = 7},
                                        new OrderLine{ itemName = "Item 09", quantity = 2},
                                    },
                                },
                                new Order{
                                    id = 9,
                                    orderLines = new OrderLine[] {
                                        new OrderLine{ itemName = "Item 01", quantity = 4},
                                        new OrderLine{ itemName = "Item 06", quantity = 17},
                                        new OrderLine{ itemName = "Item 07", quantity = 3},
                                    },
                                },
                                new Order{
                                    id = 10,
                                    orderLines = new OrderLine[] {
                                        new OrderLine{ itemName = "Item 11", quantity = 12},
                                        new OrderLine{ itemName = "Item 12", quantity = 1},
                                    },
                                },

                             };

            return orders;
        }

    }
}

If anyone knows how to proceed here, I would greatly appreciate it.
 
If you're going to sort in C# then you need to be able to compare two objects and specify which one is "smaller" and which one is "bigger". Sorting is basically making a series of such comparisons on pairs of items in a list and swapping their relative positions if appropriate until no more changes are required. So, the first step is to determine exactly what you need to do to compare two objects and determine which should come first. If you haven't already done that, do it now

Once you know how to perform that comparison, you formalise it in a method. That method should accept two objects of the appropriate type via parameters, compare them as required and then return an 'int' value indicating their relative positions. A negative value indicates that the first object is smaller than the second while a positive value indicates that the second is smaller than the first. A value of zero indicates that the two objects are equivalent for the purposes of sorting. While any positive and negative values are acceptable, it is convention to use -1 and 1 unless you have a specific reason to do otherwise. As an example, let's assume that you have a Person class with GivenName and FamilyName properties. If you have a list of Person objects that you want to sort by FamilyName and, if those are the same, by GivenName then here is what an appropriate comparison method might look like:
int ComparePeople(Person p1, Person p2)
{
    // Compare by FamilyName first.
    var result = p1.FamilyName.CompareTo(p2.FamilyName);

    if (result == 0)
    {
        // FamilyNames are the same so compare GivenName.
        result = p1.GivenName.CompareTo(p2.GivenName);
    }

    return result;
}

In this case, I am actually making use of the String.CompareTo method to do the actual comparing. That method already does what I have already described for two String objects. In this case, it does the heavy lifting and this method simply passes on the results that it returns. You should do that yourself when comparing whenever it is possible to do so. All fundamental type (string, int, double, etc) and some others implement the IComparable interface and thus inherently provide that functionality. If you can't use existing comparisons for whatever reason then you can be explicit but often you can manipulate things to use such existing comparisons. For instance, let's say that you had a type with a Level property that contains "Highest", "High", "Middle", "Low" and "Lowest". You could do this:
    var result = 0;

    switch (item1.Level)
    {
        case "Highest":
            switch (item2.Level)
            {
                case "Highest":
                    result = 0;
                    break;
                case "High":
                case "Middle":
                case "Low":
                case "Lowest":
                    result = -1;
                    break;
            }
            break;
        case "High":
            switch (item2.Level)
            {
                case "Highest":
                    result = 1;
                    break;
                case "High":
                    result = 0;
                    break;
                case "Middle":
                case "Low":
                case "Lowest":
                    result = -1;
                    break;
            }
            break;
        case "Middle":
            switch (item2.Level)
            {
                case "Highest":
                case "High":
                    result = 1;
                    break;
                case "Middle":
                    result = 0;
                    break;
                case "Low":
                case "Lowest":
                    result = -1;
                    break;
            }
            break;
        case "Low":
            switch (item2.Level)
            {
                case "Highest":
                case "High":
                case "Middle":
                    result = 1;
                    break;
                case "Low":
                    result = 0;
                    break;
                case "Lowest":
                    result = -1;
                    break;
            }
            break;
        case "Lowest":
            switch (item2.Level)
            {
                case "Highest":
                case "High":
                case "Middle":
                case "Low":
                    result = 1;
                    break;
                case "Lowest":
                    result = 0;
                    break;
            }
            break;
    }

    return result;

or you could do this:
int ComparePeople(Person item1, Person item2)
{
    var values = new[] {"Highest", "High", "Middle", "Low", "Lowest"};
    var index1 = Array.IndexOf(values, item1.Level);
    var index2 = Array.IndexOf(values, item2.Level);

    return index1.CompareTo(index2);
}

Once you've got your comparison method, you just have to decide exactly where to put it to use it in the way you want. There are a few well-defined ways to do that. I'm not going to write any more on this explicitly because I've already done so in a blog post some years ago. I suggest that you read all three parts of that. You should then put the principles I have described here and there into practice in your specific scenario. If you still have issues, by all means post back but please be specific about exactly where the issue is.
 
Thank you for replying, that's very helpful.

Just to start on things, I got the comparison to work this way:

C#:
        Person person1 = new Person();
        Person person2 = new Person();
        person1.GivenName = "John";
        person1.FamilyName = "Jones";
        person2.GivenName = "Jane";
        person2.FamilyName = "Jones";
        
        var result = person1.FamilyName.CompareTo(person2.FamilyName);
        Console.WriteLine("Here's the result: " + result);
        
        
    }
    public class Person
    {
        public string GivenName;
        public string FamilyName;
    }

When doing this, I get 1 when the results are different, and 0 when they're the same.

I was not able to get this code itself working with the first method you wrote down.

When I added the method back in, I got a compiler error:

Code:
C#:
    static void Main()
    {

        Person person1 = new Person();
        Person person2 = new Person();
        person1.GivenName = "James";
        person1.FamilyName = "Johnson";
        person2.GivenName = "Amanda";
        person2.FamilyName = "Johnson";
        
        var result = person1.FamilyName.CompareTo(person2.FamilyName);
        Console.WriteLine("Here's the result: " + result);
        var result2 = ComparePeople(person1, person2);
        Console.WriteLine("Here's the result: " + result2);
        
    }
    public class Person
    {
        public string GivenName;
        public string FamilyName;
    }
    int ComparePeople(Person p1, Person p2)
    {
        // Compare by FamilyName first.
        var result = p1.FamilyName.CompareTo(p2.FamilyName);
 
        if (result == 0)
        {
            // FamilyNames are the same so compare GivenName.
            result = p1.GivenName.CompareTo(p2.GivenName);
        }
 
    return result;
    }
Error:
C#:
jdoodle.cs(17,23): error CS0120: An object reference is required to access non-static member `Program.ComparePeople(Program.Person, Program.Person)'
Compilation failed: 1 error(s), 0 warnings

I first forgot to declare the two strings public, so I solved that issue, I'm just not sure what this error means I did wrong.
 
All you're doing there is a comparison. You seem to have ignored a lot of what I said and presumably not followed the link I provided. Comparing is not sorting. Sorting involves repeatedly comparing and swapping positions if required. As I said:
Once you've got your comparison method, you just have to decide exactly where to put it to use it in the way you want.
As I also said:
I suggest that you read all three parts of that [blog post].
The first part incorporates the comparison method into the type being compared by implementing the IComparable and IComparable<T> interfaces. The second part incorporates the comparison method into a separate, dedicated class by implementing the IComparer and IComparer<T> interfaces. The third part incorporates the comparison method into the class doing the sorting and uses the Comparison<T> delegate. In all three cases, the method doing the comparing works in exactly the same way but the comparing is not the sorting. The sorting invokes the method that does the comparing over and over and uses the result to rearrange the items in the list being sorted into the appropriate order.

I think that I probably go over this in that blog post too but I haven't reread the whole thing to check so I'll say it here. I provide three options of where to put the method that does the comparing and how to use it. It's generally not hard to decide which is the best option in any particular case.

1. Implementing IComparable and IComparable<T> in the type that will be sorted is only possible if you are the author of the type. It's also only appropriate if there is one specific way of sorting objects of that type that will always or almost always be used. If there are multiple equally sensible ways to sort objects of that type then don't implement IComparable and IComparable<T>.

2. Implementing IComparer and IComparer<T> in a separate, dedicated class is appropriate if you want to be able to sort objects of a type in multiple places in the same way or in multiple known ways. The type that implements those interfaces can be instantiated anywhere and the comparison logic is automatically available, even across multiple projects. You can also provide multiple ways to compare within the same class and choose between with, for example, a constructor argument.

3. Using the Comparison<T> delegate is appropriate when you want to perform an ad hoc sort. It can be used with a Lambda for truly one-off sorts or you can write a dedicated method and create delegates for that in multiple places if you want. If you exposed a method publicly then you could also use it across projects but I'd suggest that option 2 is more appropriate in that case.
 
Hi,

I didn't ignore what you said. I was trying to implement piecemeal what you said. I wanted to first try to understand the comparison before getting to the sorting. Since I'm not understanding the comparison, I don't think I'll get the sorting at the moment.
 
Firstly, this:
error CS0120: An object reference is required to access non-static member `Program.ComparePeople(Program.Person, Program.Person)'
is because you are calling an instance method from a static method without an instance. Your Main method is static so, in order to do this:
var result2 = ComparePeople(person1, person2);

your ComparePeople method would have to be static too. That's C# 101 and nothing to do with the problem at hand.

As for the comparing, what is it that you don't understand? I have already explained that a comparison method should return a negative number (preferably -1 but any negative integer is OK) when the first item is "less", zero when they are "equal" and a positive number (preferably 1 but any positive integer is OK) when the first item is "greater". From your code here (I have made ComparePeople static):
static void Main()
{

    Person person1 = new Person();
    Person person2 = new Person();
    person1.GivenName = "James";
    person1.FamilyName = "Johnson";
    person2.GivenName = "Amanda";
    person2.FamilyName = "Johnson";

    var result = person1.FamilyName.CompareTo(person2.FamilyName);
    Console.WriteLine("Here's the result: " + result);
    var result2 = ComparePeople(person1, person2);
    Console.WriteLine("Here's the result: " + result2);

}
public class Person
{
    public string GivenName;
    public string FamilyName;
}
static int ComparePeople(Person p1, Person p2)
{
    // Compare by FamilyName first.
    var result = p1.FamilyName.CompareTo(p2.FamilyName);

    if (result == 0)
    {
        // FamilyNames are the same so compare GivenName.
        result = p1.GivenName.CompareTo(p2.GivenName);
    }

    return result;
}

you get the following output:
Here's the result: 0
Here's the result: 1
The first comparison is done on FamilyName only and both values are "Johnson" so the two objects are considered equal. That comparison results in the value zero, exactly as it should. The first comparison is done by FamilyName first and, if those are the same (as we know they are), is then done by GivenName. The first GivenName value is "James" and the second is "Amanda". "James" comes after "Amanda" alphabetically so the first object is considered greater than the second. That comparison results in the value 1, exactly as it should.

Please explain the source of your confusion.
 
Back
Top Bottom