0

Initially I was trying to do a switch but in the next lines I will explain why it didn't work like that.

Having two arrays like those ones:

const countries = [ 'France', 'Italy', 'Spain' ];
const cities = [ 'Paris', 'Marseille', 'Rome', 'Naples', 'Milan', 'Madrid' ];

As you can see, there is a connection between the countries and the cities:

  • France has Paris and Marseille
  • Italy has Rome, Naples and Milan
  • Spain has Madrid

The logic of my application should check for those countries alphabetically, (F > I > S), then check if the available city.

If there is a country present but no city was provided, as default it should use the capital city (first from the list). If there are multiple cities, the capital should be checked for the last one.

Examples:


Input: countries = [ 'France' ]; cities = [ 'Marseille']

Result: doThis('Marseille');


Input: countries = [ 'France' ]; cities = []

Result: doThis('Paris');


Input: countries = [ 'France' ]; cities = [ 'Paris', 'Marseille']

Result: doThis('Marseille');

Here is the code:

const doThat = (city: string) => {
  console.log(city);
};

const myFunc = (countries: string[], cities: string[]) => {
  if (countries.includes('France')) {
    if (cities.includes('Marseille')) {
      doThat('Marseille');
    } else doThat('Paris');
  } else if (countries.includes('Italy')) {
    if (cities.includes('Naples')) {
      doThat('Naples');
    } else if (cities.includes('Milan')) {
      doThat('Naples');
    } else doThat('Rome');
  } else if (countries.includes('Spain')) {
    doThat('Madrid');
  } else doThat('empty');
};

It cannot work with a switch because it would be something like:

switch (countries) {
  case countries.includes('France'): ... // cannot be boolean here
...
}

Is there a way to make it better/more readable?

4
  • stackoverflow.com/questions/54590105/… Commented Aug 16, 2022 at 11:46
  • @GrafiCode That's not related at all. Commented Aug 17, 2022 at 9:11
  • @gre_gor why not? question title is "Javascript switch case with array or strings" which is basically what OP asked in this question Commented Aug 17, 2022 at 10:23
  • @GrafiCode In your question, OP seems to want to test a single value against multiple values, in here they want to test an array against a single value. And your question is ambiguous on what they actually want to test. Commented Aug 17, 2022 at 10:47

3 Answers 3

2

Here's an article I wrote about it! Refactoring with maps

Long story short: use Maps! In your specific case you may want a map of <string, string[]>.

So that in your code you will have:

for(country in countries){
  const mappedCities = contryMap.get(country)

  // find either the first city which is in both the passed array and the map 
  // or default to the first one
  const cityIndex = Math.max(0, mappedCities.findIndex(city => 
                    cities.includes(city)))

  doThat(mappedCities[cityIndex]
}

and in a separate file:

export const cityMap = new Map<string, string[]>([
['france', ['Paris', 'Marseille']],
]) 
Sign up to request clarification or add additional context in comments.

Comments

1

Try this.

const myFunc = (countries: string[], cities: string[]) => {
  for(const country of countries) {
    switch(country) {
      case "France": {
        if(cities.includes("Marseille")){
          doThat("Marseille");
          break;
        }
        // If no cities are provided, use the capital city
        doThat("Paris");
        break;
      };
      case "Italy": {
        if(cities.includes("Naples")) {
             doThat("Naples");
             break;
        } else if (cities.includes("Milan")) {
            doThat("Milan")
            break;
        } 
        // If no cities are provided, use the capital city
        doThat("Rome")
        break;
      };

      case "Spain": {
        // Since there is only one city (i.e. the capital) for Spain
        doThat("Madrid")
        break;
      }
      default: 
      // If no matching country is passed, send "empty"
        doThat("empty");
        break;
    }
  }
};

Comments

-1

Instead of hardcoding the data into the code, you should put your countries with cities into a structure, which you then choose the city.

const country_cities = {
    "France": ["Paris", "Marseille", "Lyon"],
    "Italy": ["Rome", "Naples"],
    "Spain": ["Madrid"],
};

const choose_city = (countries, cities) => {
    countries = [...countries].sort()
    for (let country of countries) {
        if (!country_cities.hasOwnProperty(country)) { // ignore unknown countries
            continue;
        }
        let [capital, ...other_cities] = country_cities[country]; // split recognised cities into the capial and other cities
        const other_city = cities.find(city => other_cities.includes(city)); // if any of the other cities was provided
        if (other_city) {
            return other_city;
        }
        return capital;
    }
    return "empty";
};

const tests = [
    [["France"], ["Marseille"], "Marseille"], // return the only country's city
    [["France"], ["Paris"], "Paris"], // return the only country's city
    [["France"], [], "Paris"], // no city, defaults to capital
    [["France"], ["Marseille", "Paris"], "Marseille"], // return non-capital
    [["France"], ["Paris", "Lyon", "Marseille"], "Lyon"], // return first non-capital
    [["France"], ["Paris", "Marseille", "Lyon"], "Marseille"], // return first non-capital
    [["Spain"], ["Barcelona"], "Madrid"], // no recognised Spanish city, defaults to capital
    [["Germany"], ["Berlin"], "empty"], // unknown country, defaults to "empty"
    [["Spain", "France"], ["Marseille", "Madrid"], "Marseille"], // return first French city
    [["Spain", "France"], ["Naples", "Madrid"], "Paris"], // no French city, defaults to capital
    [["Germany", "Italy"], ["Berlin", "Naples"], "Naples"], // Italy first recognised country, returns Italian city
    [["Germany", "Italy"], ["Berlin"], "Rome"], // Italy first recognised country, no Italian city, returns capital
];

document.querySelector("#do-this").addEventListener("click", function(e) {
  const countries = [...document.querySelectorAll("#countries option:checked")].map(o => o.value);
  const cities = [...document.querySelectorAll("#cities option:checked")].map(o => o.value);
  document.querySelector("#result-countries").textContent = countries.join(", ");
  document.querySelector("#result-cities").textContent = cities.join(", ");
  const city = choose_city(countries, cities);
  document.querySelector("#result").textContent = city;
});
document.querySelector("#run-tests").addEventListener("click", function(e) {
  const output = document.querySelector("#result-tests");
  output.textContent = "";
  for (let [countries, cities, true_result] of tests) {
    const result = choose_city(countries, cities);
    if (result === true_result) {
      output.textContent += `countries: ${countries}\ncities: ${cities}\nresult: ${result} (OK)\n\n`;
    }
    else {
      output.textContent += `countries: ${countries}\ncities: ${cities}\nresult: ${result} (should be ${true_result})\n\n`;
    }
  }
});
<div style="display: flex; gap: 0 1em;">
  <div>
    <select id="countries" multiple size="6">
      <optgroup label="Unknown">
       <option>Germany</option>
      </optgroup>
      <optgroup label="Known">
        <option>Italy</option>
        <option>Spain</option>
        <option>France</option>
      </optgroup>
    </select>

    <select id="cities" multiple size="9">
      <optgroup label="Unknown">
        <option>Berlin</option>
      </optgroup>
      <optgroup label="Known">
        <option>Paris</option>
        <option>Marseille</option>
        <option>Lyon</option>
        <option>Rome</option>
        <option>Naples</option>
        <option>Madrid</option>
      </optgroup>
    </select>
    <br>
    <button id="do-this">Do this</button>

    <div>Countries: <span id="result-countries"></span></div>
    <div>Cities: <span id="result-cities"></span></div>
    <div>Done this: <span id="result"></span></div>
  </div>
  <pre id="result-tests">
    <button id="run-tests">Run tests</button>
  </pre>
</div>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.