3

How do I write select distinct country, state from myObject in javascript.

Array.from(new Set(myObject.map(item => item.country))) would return distinct countries.. How do I add countries, states and other columns in map???

Input data:

const myObject = [
  {
    country: 'USA',
    state: 'Missouri',
    county: 'County1',
  },
  {
    country: 'USA',
    state: 'Missouri',
    county: 'County2',
  },
  {
    country: 'Canada',
    state: 'Alberta',
    county: 'County3',
  },
];

To get unique countries, I would write Array.from(new Set(myObject.map(item => item.country))) The result would be ['USA', 'Canada']

What should I write if want the result to be

[
  {
    country: 'USA',
    state: 'Missouri',
  },
  {
    country: 'Canada',
    state: 'Alberta',
  },
]

This contains unique country and state combinations

4
  • could you provide an example of the input data and the expected result? not sure if this is what you're looking for stackoverflow.com/questions/56633869/… Commented Oct 14, 2020 at 1:53
  • I edited the question with sample data and expected result. Commented Oct 14, 2020 at 2:21
  • @diedu I'm not sure why you think this question is not related to TypeScript? Commented Oct 14, 2020 at 2:23
  • I updated my answer to use your example. If it does not address your use case, please elaborate. Commented Oct 14, 2020 at 13:21

3 Answers 3

2

Below is a solution using reduce;

const myObject = [
  { country: 'USA', state: 'Missouri', county: 'County1' },
  { country: 'USA', state: 'Missouri', county: 'County2' },
  { country: 'Canada', state: 'Alberta', county: 'County3' },
];

const unique = myObject.reduce((prev, {country, state}) => 
  prev.some(x => x.country === country && x.state === state )? prev: [...prev, {country, state} ], [])

   console.log(unique )

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

Comments

1

Base on referenced answer you can use Map and spread operator as below

https://stackoverflow.com/a/63274702/4964569

const myObject = [
  { country: 'USA', state: 'Missouri', county: 'County1' },
  { country: 'USA', state: 'Missouri', county: 'County2' },
  { country: 'Canada', state: 'Alberta', county: 'County3' },
];

let data = new Map();

for (let obj of myObject) {
  delete obj.county;
  data.set(obj.country, obj);
}

let unique = [...data.values()];

console.log(unique);

2 Comments

That worked.. Thanks.. But my object has around 60 fields from which I want to consider/map 3 values in one scenario and 8 values in another scenario. Can this be updated to make it more dynamic considering the number of fields I have???
This solution will not work if you have multiple distinct country/state pairs.
1

The following code should take an array of objects and a list of keys of those objects, and return an array of objects representing the distinct values for that set of keys. I assume that the type of properties at those keys will only be string, number, or boolean.

function distinct<T extends Record<K, string | number | boolean>,
  K extends keyof T>(arr: T[], ...keys: K[]): Pick<T, K>[] {
  const key = (obj: T) => JSON.stringify(keys.map(k => obj[k]));
  const val = (obj: T) => keys.reduce((a, k) => (a[k] = obj[k], a), {} as Pick<T, K>);
  const dict = arr.reduce((a, t) => (a[key(t)] = val(t), a), {} as { [k: string]: Pick<T, K> })
  return Object.values(dict);
}

The idea is to take each object in the array, serialize the tuple of its properties at the keys in question with JSON.stringify(), and use this serialized string as a dictionary key. The value we put at that key is an object consisting of just the values at those properties. By using a dictionary, we guarantee that only one object will appear for each distinct set of properties at the keys we care about. Then we turn the values of this dictionary into an array.

If I test it on your myObject example, this is what comes out:

const result = distinct(myObject, "country", "state");
console.log(result);
/*  [{ "country": "USA", "state": "Missouri" }, { "country": "Canada", "state": "Alberta" }] */

which is what you wanted. Let's also test it on some type and array that I made up now:

interface MyObject {
  country: string,
  state: string,
  age: number,
  name: string
}
const arr: MyObject[] = [
  { name: "Alice", age: 35, country: "USA", state: "MA" },
  { name: "Bob", age: 40, country: "USA", state: "MI" },
  { name: "Carmen", age: 35, country: "Mexico", state: "BC" },
  { name: "Danilo", age: 35, country: "Mexico", state: "MI" }
]

So we have an array of MyObject. Let's get the distinct country values:

const distinctCountries = distinct(arr, "country"); // Array<{country: string}>
console.log(distinctCountries); // [{ "country": "USA" }, { "country": "Mexico" }] 
console.log(distinctCountries.map(x => x.country)); // USA, Mexico

Notice how what comes out is an array of {country: string} values. You can use map() to turn that into just an array of strings. Let's get the distinct country and state values:

const distinctCountriesAndStates = distinct(arr, "country", "state");
console.log(distinctCountriesAndStates);
/* [{ "country": "USA", "state": "MA" }, { "country": "USA", "state": "MI" }, 
{ "country": "Mexico", "state": "BC" }, { "country": "Mexico", "state": "MI" }] */

Here it's an array of {country: string, state: string} objects. Not sure how you want to represent those, but you can use map() to massage them as you see fit. Finally let's get the distinct country and age values:

const distinctAgesAndCountries = distinct(arr, "country", "age");
console.log(distinctAgesAndCountries);
/* [{ "country": "USA", "age": 35 }, { "country": "USA", "age": 40 },
 { "country": "Mexico", "age": 35 }] */

That's an array of {country: string, age: number} objects.

Anyway, hope that gives you some direction.

Playground link to code

1 Comment

Thanks for taking time to explain it in an elaborative way. This works very well for me. And this distinct method accepts dynamic parameters which makes my work very easy as I have to use it for different parameters each time.

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.