0

Let's say I have the following CSV

Sydney,Dubai,1
Dubai,Venice,2
Venice,Rio,3
Venice,Sydney,1
Sydney,Rio,7

First field is From second is To and third is Duration.

I need a method which can take a From input and spit out the shortest path to all other To field in the following format-

Selected City: Sydney
To 1: Dubai, Smallest Path Length: 1, Path: Sydney, Dubai.
To 2: Venice, Smallest Path Length: 3, Path: Sydney, Dubai, Venice.
To 3: Rio, Smallest Path Length: 6, Path: Sydney, Dubai, Venice, Rio.

(N.B. Sydney-Rio is 7 hours long hence Sydney-Dubai-Venice-Rio
 is the shortest route here which takes 2 hours).

I haven't got any code to add here plus others have suggested to use Dijkstra's algorithm but so far I haven't got an example which accomplishes what I need.

envyM6
  • 1,099
  • 3
  • 14
  • 35

2 Answers2

2

I have written a small console program which satisfies your need. it is very basic and ca be enhanced further if needed.

if you need a downloadable solution please let me know.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ShortPath

{
    class Program
    {
        static void Main(string[] args)
        {
 // assuming you have loaded your CSVs into a list of string
            List<string> csvLines = new List<string>()
            {
                "Sydney,Dubai,1",
                "Dubai,Venice,2",
                "Venice,Rio,3",
                "Venice,Sydney,1",
                "Sydney,Rio,7"
            };



            // lets convert the list of string into list or route
            var routes = new List<Route>();
            csvLines.ForEach(s =>
            {
                // split by ,
                string[] pieces = s.Split(',');

                // ensure travel time is a number
                decimal travelTime = 0;
                decimal.TryParse(pieces[2], out travelTime);

                // convert string to route object
                routes.Add(new Route()
                {
                    From = pieces[0],
                    To = pieces[1],
                    TravelTime = travelTime
                });
            });

            // once all the data in place - the rest is easy.
            // lets assume our FROM is sydne
            string selectedFrom = "Sydney";

            // no lets find all the routes from sydney to every other place
            // listing the shortes route first
            // the "Where" clause allows us to filter by the selected from
            // the order by clause allows us to order by travel time
            var desiredRoutes = routes.Where(route => route.From == selectedFrom).OrderBy(route => route.TravelTime).ToList();

            // the output template allows us to format all outputs
            // the numbers in culry bracers such as {0} {1}...etc are placeholderst that get replaced with actul values
            // {0} = index number
            // {1} = To
            // {2} = duration
            // {3} = From
            // "To 1: Dubai, Smallest Path Length: 1, Path: Sydney, Dubai.";/
            string outputTemplate = "To {0}: {1}, Smallest Path Length: {2}, Path: {3}, {1}.";


            Console.WriteLine("Selected Country: '{0}'.", selectedFrom);

            // look through each selected route
            for(int index = 0; index < desiredRoutes.Count; index++)
            {
                // ensure you access to the route variable in the current instance of the loop
                var route = desiredRoutes[index];

                // write all outputs
                // (index + 1) allows our counter to start from 1 instead of 0
                Console.WriteLine(outputTemplate, (index + 1), route.To, route.TravelTime, route.From);
            }

            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
    }
}

NOTE: the class for Route:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ShortPath
{
    public class Route
    {
        public string From { get; set; }

        public string To { get; set; }

        public decimal TravelTime { get; set; }

    }
}

The output should look as follows:

Selected Country: 'Sydney'.
To 1: Dubai, Smallest Path Length: 1, Path: Sydney, Dubai.
To 2: Rio, Smallest Path Length: 7, Path: Sydney, Rio.
Press any key to exit.
WaseemS
  • 149
  • 2
  • 7
  • Yes please I need the downloadable solution. But it appears you have hard coded the routes here rather than importing it from the CSV. so lets say I have the CSV in a List. How do I go about and use that? – envyM6 Sep 18 '16 at 19:01
  • 1
    It does appear my solution caters for an ideal scenario. I will modify this for CSV input – WaseemS Sep 18 '16 at 19:03
  • 1
    There you go - I will upload the solution soon (updated answer) – WaseemS Sep 18 '16 at 19:11
  • 2
    you can find the downloadable solution here: http://blog.waseem-sabjee.com/wp-content/uploads/2016/09/ShortPath.zip note it was created using VS 2013. if you need me to save into an older format please let me know. – WaseemS Sep 18 '16 at 19:13
  • NOTE: this is not the most durable code - we would need to look at NULL checks and length checks to ensure safety – WaseemS Sep 18 '16 at 19:15
  • This is brilliant. Of course I'll add checks!. Thanks alot Waseem! – envyM6 Sep 18 '16 at 19:24
  • @Missy Thank you for your recognition. it means a lot! – WaseemS Sep 18 '16 at 20:32
  • @envyM6 always a pleasure. – WaseemS Sep 18 '16 at 20:33
  • I've discovered a small problem here. It only calculates the direct path. I.e. Sydney-Dubai. However I also need to calculate the via path, such as Sydney-Dubai-Venice-Rio which takes 6 instead of 7 and the shortest. Possible Edits anyone? – envyM6 Sep 18 '16 at 21:08
  • HI, sorry I had to be at work. I have updated the code to do recursive searching of routes. please see http://blog.waseem-sabjee.com/wp-content/uploads/2016/09/ShortPathRecursive.rar – WaseemS Sep 19 '16 at 17:26
0

Try this

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;


namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            //this one uses strings as node names
            Dijkstra1.Program.Dijkstra();
            Console.ReadLine();
        }
    }
}
namespace Dijkstra1
{
    class Program
    {
        //Sydney,Dubai,1
        //Dubai,Venice,2
        //Venice,Rio,3
        //Venice,Sydney,1
        //Sydney,Rio,7
        static List<List<String>> input1 = new List<List<string>>{
               new List<String>() {"Sydney","0","1","7","1"},
               new List<String>() {"Dubai", "1","0","0","2"},
               new List<String>() {"Rio",   "7","0","0","3"},
               new List<String>() {"Venice","1","2","3","0"},
            };

        static public void Dijkstra()
        {
            CGraph cGraph;
            cGraph = new CGraph(input1);
            Console.WriteLine("-------------Input 1 -------------");
            cGraph.PrintGraph();

        }
        class CGraph
        {
            List<Node> graph = new List<Node>();
            public CGraph(List<List<String>> input)
            {
                foreach (List<string> inputRow in input)
                {
                    Node newNode = new Node();
                    newNode.name = inputRow[0];
                    newNode.distanceDict = new Dictionary<string, Path>();
                    newNode.visited = false;
                    newNode.neighbors = new List<Neighbor>();
                    //for (int index = 1; index < inputRow.Count; index++)
                    //{
                    //    //skip diagnol values so you don't count a nodes distance to itself.
                    //    //node count start at zero
                    //    // index you have to skip the node name
                    //    //so you have to subtract one from the index
                    //    if ((index - 1) != nodeCount)
                    //    {
                    //        string nodeName = input[index - 1][0];
                    //        int distance = int.Parse(inputRow[index]);
                    //        newNode.distanceDict.Add(nodeName, new List<string>() { nodeName });
                    //    } 
                    //}
                    graph.Add(newNode);
                }
                //initialize neighbors using predefined dictionary
                for (int nodeCount = 0; nodeCount < graph.Count; nodeCount++)
                {
                    for (int neighborCount = 0; neighborCount < graph.Count; neighborCount++)
                    {
                        //add one to neighbor count to skip Node name in index one
                        if (input[nodeCount][neighborCount + 1] != "0")
                        {
                            Neighbor newNeightbor = new Neighbor();
                            newNeightbor.node = graph[neighborCount];
                            newNeightbor.distance = int.Parse(input[nodeCount][neighborCount + 1]);
                            graph[nodeCount].neighbors.Add(newNeightbor);
                            Path path = new Path();
                            path.nodeNames = new List<string>() { input[neighborCount][0] };
                            //add one to neighbor count to skip Node name in index one
                            path.totalDistance = int.Parse(input[nodeCount][neighborCount + 1]);
                            graph[nodeCount].distanceDict.Add(input[neighborCount][0], path);
                        }
                    }
                }

                foreach (Node node in graph)
                {
                    foreach (Node nodex in graph)
                    {
                        node.visited = false;
                    }
                    TransverNode(node);
                }
            }
            public class Neighbor
            {
                public Node node { get; set; }
                public int distance { get; set; }
            }
            public class Path
            {
                public List<string> nodeNames { get; set; }
                public int totalDistance { get; set; }
            }
            public class Node
            {
                public string name { get; set; }
                public Dictionary<string, Path> distanceDict { get; set; }
                public bool visited { get; set; }
                public List<Neighbor> neighbors { get; set; }
            }
            static void TransverNode(Node node)
            {
                if (!node.visited)
                {
                    node.visited = true;
                    foreach (Neighbor neighbor in node.neighbors)
                    {
                        TransverNode(neighbor.node);
                        string neighborName = neighbor.node.name;
                        int neighborDistance = neighbor.distance;
                        //compair neighbors dictionary with current dictionary
                        //update current dictionary as required
                        foreach (string key in neighbor.node.distanceDict.Keys)
                        {
                            if (key != node.name)
                            {
                                int neighborKeyDistance = neighbor.node.distanceDict[key].totalDistance;
                                if (node.distanceDict.ContainsKey(key))
                                {
                                    int currentDistance = node.distanceDict[key].totalDistance;
                                    if (neighborKeyDistance + neighborDistance < currentDistance)
                                    {
                                        List<string> nodeList = new List<string>();
                                        nodeList.AddRange(neighbor.node.distanceDict[key].nodeNames);
                                        nodeList.Insert(0, neighbor.node.name);
                                        node.distanceDict[key].nodeNames = nodeList;
                                        node.distanceDict[key].totalDistance = neighborKeyDistance + neighborDistance;
                                    }
                                }
                                else
                                {
                                    List<string> nodeList = new List<string>();
                                    nodeList.AddRange(neighbor.node.distanceDict[key].nodeNames);
                                    nodeList.Insert(0, neighbor.node.name);
                                    Path path = new Path();
                                    path.nodeNames = nodeList;
                                    path.totalDistance = neighbor.distance + neighborKeyDistance;
                                    node.distanceDict.Add(key, path);
                                }
                            }
                        }
                    }
                }
            }
            public void PrintGraph()
            {
                foreach (Node node in graph)
                {
                    Console.WriteLine("Node : {0}", node.name);
                    foreach (string key in node.distanceDict.Keys.OrderBy(x => x))
                    {
                        Console.WriteLine(" Distance to node {0} = {1}, Path : {2}", key, node.distanceDict[key].totalDistance, string.Join(",", node.distanceDict[key].nodeNames.ToArray()));
                    }
                }
            }
        }
    }

}
jdweng
  • 33,250
  • 2
  • 15
  • 20
  • Hi thank you very much this seems to work but can you please explain the code a bit? or add more comments explaining? Im scratching my head over this... – envyM6 Sep 19 '16 at 08:55
  • It is standard Dijkstra algorithm. Each location is a Node class and then Node has a list of neighbors (which is a route between two cities) with distance to each neighbor. Each Node is initially set to visited = false. Then a recursive algorithm is used to enumerate each Node's neighbor list and stopping when a Node is marked as visited. – jdweng Sep 19 '16 at 09:33
  • how did you come up with `input1`? as in `new List() {"Sydney","0","1","7","1"}` and so on – envyM6 Sep 19 '16 at 09:39
  • 1
    The number in the columns are the distance to each city in the same order that the cities are listed in column 1. So 7 is the distance from Sydney to Rio, or Rio to Sydney. The table is symmetrical around the diagonal. – jdweng Sep 19 '16 at 09:56