DEV Community

Daily Challenge #6 - Grandma and her friends

dev.to staff on July 03, 2019

Another day, another challenge. Today, your task is to help your grandmother visit her friends. This challenge comes from user @g964 on CodeWars:...
Collapse
 
shayau profile image
Shaya • Edited

I think that some of the details are missing here...
What are the distances?

Collapse
 
ryansmith profile image
Ryan Smith

I was able to find codewars.com/kata/help-your-granny... which has additional information.

Collapse
 
coreyja profile image
Corey Alexander

Ya agreed...
Feels like we are missing some kind of map, or distances between different towns

Collapse
 
spaciecat profile image
Spacie • Edited

I'm making the following assumptions, but I think I have a solution:

  • Town X1 is 1 mile from X0, X2 is 2 miles from X0, etc... (otherwise I don't believe there is enough information to come up with a solution)
  • friendTownList uses format C (flattened list)
  • the return trip from the last friend's house back to X0 is included in the total

Could be one (ugly) line if not counting error handling in the case where we don't know where a friend lives, could also be smaller if friendTownList was a hash to start with~

def getDistance(visitOrder, friendTownList)
  townHash = Hash[*friendTownList]
  visitOrder.each {|f| raise "IDK where #{f} lives!" if !townHash.key? f }
  [0, *visitOrder.map(&proc {|f| townHash[f][1 .. -1].to_i }), 0]
    .each_cons(2)
    .map(&proc {|a, b| (a - b).abs })
    .sum
end

getDistance(%w(A2 A1 A3), %w(A1 X1 A2 X2 A3 X3))
# -> 8
# Because (0, 2[2], 1[1], 3[2], 0[3]) -> 2 + 1 + 2 + 3 = 8

I'll update / post another solution if the question is updated with more information!

Collapse
 
coreyja profile image
Corey Alexander

Wow! Love that we are all over here just complaining about not having enough data and you just went for it!

Love it! Keep up the awesome work on these challenges!

Collapse
 
stevemoon profile image
Steve Moon • Edited

Erlang:
note: I followed the link to the codewars page and am using their list of distances. I also assume Granny is coming home after visiting her friends. A different list of distances can be passed in, without limit on length. But if the correlation between friend1 is in town1 breaks down then I'd need to add a map/hashtable for that lookup.

-module(devto6).
-export([find_distance/1]).

find_distance([B, C | Rest]) ->
    TotalDist = B,
    A = math:sqrt(C * C - B * B),
    find_distance(TotalDist + A, C, Rest).

find_distance(TotalDist, B, [C | Rest]) ->
    A = math:sqrt(C * C - B * B),
    find_distance(TotalDist + A, C, Rest);
find_distance(TotalDist, C, []) ->
    TotalDist + C.

devto6:find_distance([100, 200, 250, 300]).
889.0363202746577

Collapse
 
valerionarcisi profile image
Valerio Narcisi • Edited

//TS
//TDD


enum Map {
    X1 = 100.0,
    X2 = 200.0,
    X3 = 250.0,
    X4 = 300.0,
}

const grannyFriends = [
    ['A1', 'X1'],
    ['A2', 'X2'],
    ['A3', 'X3'],
    ['A4', 'X4'],
];

export default (friends: string[]): number => {
    return friends
        .map(f => {
            for (const gf of grannyFriends) {
                if (gf.includes(f)) {
                    return Map[gf[1]];
                }
            }
            return 0;
        })
        .reduce((acc, curr) => (curr > 0 ? acc + (curr - acc) : acc), 0);
};

TEST

import grannyTrip from './help-your-granny';

describe('tdd granny trip', () => {
    test('should calculate distances', () => {
        expect(grannyTrip(['A1', 'A2', 'A3', 'A4', 'A5'])).toEqual(300);
        expect(grannyTrip(['A1', 'A2', 'A3'])).toEqual(250);
        expect(grannyTrip(['A1', 'A2', 'A4'])).toEqual(300);
    });
});
Collapse
 
celyes profile image
Ilyes Chouia • Edited

this is my solution in PHP, although the description is quite ambiguous...

$friends = [["A1", "X1"], ["A2", "X2"], ["A3", "X3"], ["A4", "X4"]];
$distances = [["X1", 100], ["X2", 200], ["X3", 350], ["X4", 420]];

$distances = array_reduce($distances, function($x, $y){
    $x[$y[0]] = $y[1]; 
    return $x;
});

function getTotalDistance($friends, $distances){
    $total = 0;
    for($x = 0; $x < count($friends); $x++){
        if(count($friends[$x]) === 2){
            $total += ($distances[$friends[$x][1]] * 2);
        }
    }
    return $total;
}
echo getTotalDistance($friends, $distances);
Collapse
 
margo1993 profile image
margo1993
package utils

import (
    "errors"
    "math"
)

type CityDistance struct {
    City string
    Distance float64
}

func VisitFriend(friendsToVisit []string, friendsCities [][]string, cityDistances []CityDistance) (int, error) {
    totalDistance := 0.0

    var lastCityName string
    for _, friend := range friendsToVisit {
        friendCity, e := findFriendCity(friend, friendsCities)

        if e != nil {
            return 0, e
        }

        if lastCityName == "" {
            distanceToCity, e := findDistanceToCity(friendCity, cityDistances)

            if e != nil {
                return 0, e
            }

            totalDistance += distanceToCity
        } else {
            distanceBetweenCities, e := findDistanceBetweenCities(lastCityName, friendCity, cityDistances)

            if e != nil {
                return 0, e
            }

            totalDistance += distanceBetweenCities
        }

        lastCityName = friendCity
    }
    distanceBackToHome, _ := findDistanceToCity(lastCityName, cityDistances)

    totalDistance += distanceBackToHome
    return int(math.Floor(totalDistance)), nil
}

func findDistanceToCity(city string, cityDistances []CityDistance) (float64, error) {
    for _, cityDistance := range cityDistances {
        cityName := cityDistance.City
        distance := cityDistance.Distance

        if cityName == city {
            return distance, nil
        }
    }

    return 0, errors.New("Didn't find city")
}

func findDistanceBetweenCities(fromCity string, toCity string, cityDistances []CityDistance) (float64, error) {
    totalDistance := 0.0

    lastDistance := 0.0;
    for _, cityDistance := range cityDistances {
        city := cityDistance.City
        distance := cityDistance.Distance

        if lastDistance > 0 {
            totalDistance += math.Sqrt((lastDistance * lastDistance) + (distance * distance))
        }

        if (city == fromCity || city == toCity) && lastDistance > 0 {
            return totalDistance, nil
        }

        if (city == fromCity || city == toCity) || lastDistance > 0 {
            lastDistance = distance
        }
    }

    return 0, errors.New("Didn't find city")
}

func findFriendCity(friend string, friendCities [][]string) (string, error) {
    for _, friendCity := range friendCities {
        if friend == friendCity[0] {
            return friendCity[1], nil
        }
    }

    return "", errors.New("Didn't found city for friend!")
}
Collapse
 
kesprit profile image
kesprit • Edited

My solution in Swift, first time I use nested functions :

let distanceReferences = [ "X1": 100.0, "X2": 200.0, "X3": 250.0, "X4": 300.0 ]
let friendsReferences = [ "A1": "X1", "A2": "X2", "A3": "X3", "A4": "X4" ]
let friendsOrder = [ "A1", "A2", "A3", "A4", "A5"]

func grandmaTravel(friendsToVisit: [String]) -> Int {
    func distanceBetween(firstPoint: String, to secondPoint: String) -> Int {
        let first = distanceReferences.first {
            $0.key == firstPoint
        }

        let second = distanceReferences.first {
            $0.key == secondPoint
        }

        guard let distanceToFirst = first?.value, let distanceToSecond = second?.value
            else { return 0 }
        let result = (pow(distanceToSecond, 2) - pow(distanceToFirst, 2)).squareRoot()

        return Int(result.rounded())
    }

    var distance = 0
    var points = friendsToVisit.compactMap {
        friendsReferences[$0]
    }
    var currentPoint = points.removeFirst()

    // X0 to fist town
    distance += Int(distanceReferences[currentPoint] ?? 0)

    points.forEach {
        distance += distanceBetween(firstPoint: currentPoint, to: $0)
        currentPoint = $0
    }

    // last town to X0
    distance += Int(distanceReferences[currentPoint] ?? 0)

    return distance
}

grandmaTravel(friendsToVisit: friendsOrder)
Collapse
 
alvaromontoro profile image
Alvaro Montoro

JavaScript

let friends = [["A1", "X1"], ["A2", "X2"], ["A3", "X3"], ["A4", "X4"]];
let distances = [["X1", 100.0], ["X2", 200.0], ["X3", 250.0], ["X4", 300.0]];

// transform the array into an object to make it easier to process later
// solution from https://stackoverflow.com/q/26454655/3695983
distances = distances.reduce(function(p, c) { p[c[0]]=c[1]; return p; }, {});

function calculateDistance(friends, distances) {
  let total = 0;
  for (let x = 0; x < friends.length; x++) 
    if (friends[x].length === 2) 
      total += (distances[friends[x][1]] * 2);
  return total;
}

It doesn't use a smart algorithm to calculate the distance: grandma will visit a friend and then go back home, and continue with the next friend. If she doesn't know the city where a friend lives, she'll stay at home and meet the next friend.

Another version using array methods (and kind of unreadable):

let distances = { "X1": 100.0, "X2": 200.0, "X3": 250.0, "X4": 300.0 };
let friends = [ ["A1", "X1"], ["A2", "X2"], ["A3", "X3"], ["A4", "X4"] ];

const calculateDistance = (friends, distances) => friends.reduce((acc, curr) => curr.length === 2 ? acc + (distances[curr[1]] * 2) : acc, 0);

Here is a demo on CodePen.

Collapse
 
alvaromontoro profile image
Alvaro Montoro

This is a bit confusing. If you don't know the next city to visit, how will you calculate the distance between the two cities?

Collapse
 
dylanesque profile image
Michael Caveney

The problem says that the cities must be visited in the order in which they appear in the array, but yes, details are missing, we don't know the distance between the cities, which is necessary information to solve the problem.

Collapse
 
alvaromontoro profile image
Alvaro Montoro

It's even worse: "It can happen that we don't know the town of one of the friends." Even if we had the distances, we may not even know which is our next destination.

Collapse
 
coreyja profile image
Corey Alexander

Take this comment as a commitment to do this challenge sometime over the holiday weekend, pinky promise!

Collapse
 
coreyja profile image
Corey Alexander

Here I am to fulfill my promise!

I followed the directions at: codewars.com/kata/help-your-granny...

Buttt... Even then they left something to be desired lol

But I made do! I made a few assumptions, changes to the rules.

The one rule I ignored (because honestly I didn't read it till later) is:

If a town Xi is not visited you will suppose that the triangle
X0Xi-1Xi+1 is still a right triangle.

I'm gonna justify it by saying that this rule means that the map changes depending on where Granny goes. I didn't think granny's plan should alter the map, but also I kinda just didn't read that rule!

I also created the rule that there are only roads between adjacent towns, and there is a road from each down to your grannies Home town. This makes the map a lot like the wheel and spoke map that is drawn in the linked problem.

These two rules combine to mean that not every map is valid, so I return an Error when we encounter an invalid map.

This change of rules, actually led to some more complexity lol. Now it wasn't straight forward what the best path from any arbitrary town to another was. Maybe you should travel "town to town" and maybe it was better to go home in the middle.
I take whichever of these two paths turns out to be shorter!

It's close to 200 lines, so instead of posting it here I'm just gonna post a link to the Github!

github.com/coreyja/dev-to-challeng...