Javascript function to show routes of the cities followed

Viewed 1339

I am practising coding on codewars.com. I came across this question:

We are tracking down our rogue agent Matthew Knight A.K.A. Roy Miller and he travels from places to places to avoid being tracked. Each of his travels are based on a list of itineraries in an unusual or incorrect order. The task is to determine the routes he will take in his every journey. You are given an array of routes of his itineraries. List down only the places where he will go in correct order based on his itineraries.

Example: routes = [[USA, BRA], [JPN, PHL], [BRA, UAE], [UAE, JPN]]

result: "USA, BRA, UAE, JPN, PHL"

I have tried with the code below:

function findRoutes(routes) {

    let newArr = Array.prototype.concat.apply([], routes)
    let unique = newArr.filter((v, i, a) => a.indexOf(v) === i);

    return unique.join(', ');
  
}
console.log(findRoutes([["USA","BRA"],["JPN","PHL"],["BRA","UAE"],["UAE","JPN"]]))

It only worked for the case given but when I tried aginst another test case:

[["Chicago", "Winnipeg"], ["Halifax", "Montreal"], ["Montreal", "Toronto"], ["Toronto", "Chicago"], ["Winnipeg", "Seattle"]]

If failed...Therefore I knew something is wrong since the movement should be orderly. I would be glad to improve it or rewrite from a different idea.

4 Answers

Here's one solution. I won't say this is the best tactic, but it works. This is how I made it:

  1. Find the first city (the one that doesn't have any item starting with its name), with a double loop
  2. Remove the element containing the first city from the routes array
  3. Iterate through the routes array to find the next city and remove the element, until the list is empty
  4. Add the last "next" city you were looking for (second city of the last element)

let routes = [["Chicago", "Winnipeg"], ["Halifax", "Montreal"], ["Montreal", "Toronto"], ["Toronto", "Chicago"], ["Winnipeg", "Seattle"]]
let solution = [];
let next = '';

for(i = 0; i < routes.length; i++) {
  let first = routes[i][0];
  next = routes[i][1];
  let j = 0;
  while (j < routes.length && routes[j][1] !== first) {
    j++;
  }
  if (j >= routes.length) {
    solution.push(first);
    routes.splice(i, 1);
    break;
  }
}

while (routes.length !== 0) {
  for(i = 0; i < routes.length; i++) {
    if (routes[i][0] === next) {
      solution.push(routes[i][0]);
      next = routes[i][1]
      routes.splice(i, 1);
      break;
    }
  }
}
solution.push(next);
console.log(solution);

I'd like to precise that this algorithm will have serious issue with infinite loop if your list makes impossible to get a solution. For example, routes = [[A, B], [C, D]] will fail and never stop.

It can be done as follow:

function findRoutes(routes) {
  let result = '';
  let next = '';
  const routesArr = routes.reduce((returnArr, arr) => {
      returnArr[0].push(arr[0]);
      returnArr[1].push(arr[1]);
      return returnArr;
  }, [[], []]);

  const start = routesArr[0].filter(str => 
  !routesArr[1].includes(str))[0];
  result += start;
  next = start;

  for(let i = 0; i < routesArr[0].length; i++) {
    const index = routesArr[0].indexOf(next);
    next = routesArr[1][index];
    result += `, ${next}`;
  }

  return result;
}

Here's an algorithm that follows destination by destination:

function findFrom(routes, dest) {
  let from = null;
    routes.forEach(function(cv, i, arr) {
    if (cv[1] == dest) {
      //console.log('found - ' + cv[1]);
      from = cv[0];
    }
  });
  return from;
}

function findDest(routes, from) {
  let dest = null;
    routes.forEach(function(cv, i, arr) {
    if (cv[0] == from) {
      //console.log('found - ' + cv[1]);
      dest = cv[1];
    }
  });
  return dest;
}

function findRoutes(routes) {
    var output = [];
  //start with first one
  if (routes.length == 0) {
    return output;
  }
  
  //find starting point
  var firstFrom = null;
  var from = routes[0][0]; 
  var dest = null;
  var counter = 0;
  do {    
    firstFrom = findFrom(routes, from);
    if (firstFrom != null) {
      from = firstFrom
    }
    counter++;
  } while (firstFrom != null && counter < routes.length);
  
  //write down starting point
  output.push(from);
  
  //find subsequent elements
  counter = 0; //reset
  do {
    dest = findDest(routes, from);
    if (dest != null) {
      output.push(dest);
      from = dest;
    }
    counter++;
  } while (dest != null && counter < routes.length);
  return output;
}

console.log(findRoutes([["USA","BRA"],["JPN","PHL"],["BRA","UAE"],["UAE","JPN"]]));

console.log(findRoutes([["Chicago", "Winnipeg"], ["Halifax", "Montreal"], ["Montreal", "Toronto"], ["Toronto", "Chicago"], ["Winnipeg", "Seattle"]]));

I saved it in this fiddle

This is the result: enter image description here

How does it work? It looks for a starting point by any given element (we'll start with the first one). Having found a starting point, all the subsequent elements can be found. It looks at the pairs given and walks through the array once for every step. Once you have a city, you get the destination from the pair. Now look again through all of the array with the destination as a starting point.

I also added a check for cycles. There's a counter variable that will stop any search once the full length of the array has been looked through. So it will loop only once through the destinations if they contain a loop.

What's wrong with yours? You find the first index of the city concatenated like this: enter image description here

To find the first city I found the difference between beginning and end

Solution on java;

    List<String> beginning = new ArrayList();
    List<String> end = new ArrayList();
    HashMap<String, String> hashMap = new HashMap<>();

    for (int i = 0; i < routes.size(); i++) {
        beginning.add(routes.get(i).get(0));
        end.add(routes.get(i).get(1));
        hashMap.put(routes.get(i).get(0), routes.get(i).get(1));
    }

    beginning.removeAll(end); // the difference of list

    String result = beginning.get(0) + ", ";// first place
    String nextOne = hashMap.get(beginning.get(0));
    while (hashMap.get(nextOne) != null) {
        result += nextOne + ", ";
        nextOne = hashMap.get(nextOne);
    }
    result += nextOne;

    return result;
Related