1

I have an array called people which is an array of objects(person's name and his/her city's name), and i want to make a function that calculate the total number of distinct cities in that array. I used a function that use for loop but it seems to be a better way by using reduce functions in javascript. Here is the snippet

const people = [
  { name: "Jessica", city: "New York"},
  { name: "Steve",   city: "Los Angels"},
  { name: "Peter",   city: "Boston"},
  { name: "Elaine",  city: "Montreal"},
  { name: "Chris",  city: "Montreal"},
  { name: "Mike",  city: "Boston"},
  { name: "George",  city: "Vancouver"},
];
let nbre_distinct_cities = 0;

countDistinctCity(people);
console.log('Total number of distinct cities: ',nbre_distinct_cities); 

function countDistinctCity(people)
{
  for(let i = 0; i < people.length; i++)
  {
      if(i === people.length - 1)
      {
          break;
      }
      else if(people[i].city !== people[i + 1].city)
      {
        nbre_distinct_cities++
      }
  }

}

I would appreciate if someone suggest an efficient function using reduce() function

5
  • What's the point of that first if statement? What if the last city in the list is unique? Commented Jul 31, 2019 at 15:12
  • 1
    @Pointy It's because the logic is wrong to avoid an index out of bounds. This logic doesn't count the unique entries. Consider using a Set Commented Jul 31, 2019 at 15:14
  • 1
    @Pointy. i use the if just to make sure i don't compare with undefined element. i compare previous element and next element of the array Commented Jul 31, 2019 at 15:14
  • 2
    In regards to your title and the last line of your question: every() certainly does not apply here. Check out the documentation. reduce() is closer, but you could probably just .map() the cities into a new Set and get its size. Commented Jul 31, 2019 at 15:16
  • @ChristopherSchneider oh right, I see now. Yea this is just all wrong; a Set (or even a simple object since the city names are just strings) would work. Commented Jul 31, 2019 at 15:21

4 Answers 4

6

You can use a Set to store all the cities from the array and, since a set only has unique entries, the final size of the set will give you the number of distinct cities:

const people = [
  { name: "Jessica", city: "New York"},
  { name: "Steve",   city: "Los Angels"},
  { name: "Peter",   city: "Boston"},
  { name: "Elaine",  city: "Montreal"},
  { name: "Chris",  city: "Montreal"},
  { name: "Mike",  city: "Boston"},
  { name: "George",  city: "Vancouver"},
];

let nbre_distinct_cities = new Set(people.map(({city}) => city)).size;
console.log('Total number of distinct cities: ', nbre_distinct_cities); 

Sign up to request clarification or add additional context in comments.

Comments

1

Using reduce

Object.keys(people.reduce((acc, ppl) => (acc[ppl.city] = ppl.city, acc), {})).length

Comments

0

you can solve the problem via reduce method

    const cities = people.reduce((accumulator, current) => {
       const isItNotExistInAccumulator
          = accumulator.every(city => city !== current.city);
       if (isItNotExistInAccumulator) return [current.city, ...accumulator];
       return accumulator;
    }, []);

    console.log(cities.length);

7 Comments

This would work but I'd suggest a simple accumulator.indexOf(current.city) < 0 instead of using .every(), personally.
accumulator.every is O(n^2), looping repeatedly over the accumulator for each and every element. Set or object is O(n), with a constant time check for adding each element (and less verbose). indexOf(current.city >= 0) suffers from the same problems.
@ggorlen I wasn't arguing that this answer was better than the other - in fact I suggested the very same Set implementation in the comments above. Though, indexOf is at least more efficient than .every(), as it doesn't require the invocation of a callback method. I'm simply noting that using .every() to check if a string exists in an array is never a great choice.
@ggorlen Again, you seem to be debating something that we agree upon. I myself suggested the Set method in the comments on OP before there were any answers, because I agree - it is the best one, given its time complexity. However, I am telling Max that you should never use .every() to determine if a string exists in an array. I don't think this is the best answer regardless, but it's still a valuable takeaway.
Using .every() reimplements functionality found elsewhere. Using .indexOf would be preferable, or .includes() is better yet. Even using .some() would arguably be clearer than .every().
|
0

An alternative solution that removes the duplicates with Array.indexOf

const people = [
  { name: "Jessica", city: "New York"},
  { name: "Steve",   city: "Los Angels"},
  { name: "Peter",   city: "Boston"},
  { name: "Elaine",  city: "Montreal"},
  { name: "Chris",  city: "Montreal"},
  { name: "Mike",  city: "Boston"},
  { name: "George",  city: "Vancouver"},
];

let nbre_distinct_cities = people.map(el => el.city)
 .filter((city, idx, arr) => arr.indexOf(city) === idx).length;

console.log('Total number of distinct cities: ', nbre_distinct_cities); 

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.