0

I have an array in javascript which contains a number of nested arrays

let data = [
  ["Apple","Fruit","Red"],
  ["Pear","Fruit","Green"],
  ["Orange","Fruit","Orange"],
  ["Carrot","Vegetable","Orange"],
  ["Pea","Vegetable","Green"],
  ["Pumpkin","Vegetable","Orange"]
]

From this array, I wish to create two new arrays. Arr1 is the unique food types (index2) and Arr2is the unique colors (Index 3).

My new arrays should be:

Arr1 = ["Fruit","Vegetable"]
Arr2 = ["Red","Green","Orange"]

I have managed to achieve this by using for each, where I've pushed every second object to an array. I then filter this new array for unique values. This is the code I'm using to do this:

var Foods = []
var Colors = []

for (var key in data) {
          Foods.push(data[key][1]);
}         


for (var key in data) {
          Colors.push(data[key][2]);
}  


let Arr1 = [...new Set(Foods)]
let Arr2 = [...new Set(Colors)]


console.log(Arr1)
console.log(Arr2)

Although this works well, as a javascript beginner, I thought there may be a more elegant way to achieve.

For example is it not possible to filter all the unique values of data with an index of [2]?

1
  • 2
    sets are best option when you want to deal with duplicates. Commented May 1, 2021 at 18:23

4 Answers 4

2

Your code is fine as it is, the only improvement would be to replace loops with map:

let foods  = [...new Set(data.map(d => d[1]))]
let colors = [...new Set(data.map(d => d[2]))]

There's absolutely no need to overcomplicate matters with reduce, destructuring and so on.

If you want it to look slightly more elegant, you can define two utility functions

const item = i => a => a[i]
const uniq = a => [...new Set(a)]

and then

let foods  = uniq(data.map(item(1)))
let colors = uniq(data.map(item(2)))

Another option:

const uniq = a => [...new Set(a)]
const zip = (...args) => args[0].map((_, i) => args.map(a => a[i]))

let [_, foods, colors] = zip(...data).map(uniq)
Sign up to request clarification or add additional context in comments.

Comments

2

Reduce the array to two Sets, and then destructure the Sets, and spread each into the respective array:

const data = [["Apple","Fruit","Red"],["Pear","Fruit","Green"],["Orange","Fruit","Orange"],["Carrot","Vegetable","Orange"],["Pea","Vegetable","Green"],["Pumpkin","Vegetable","Orange"]]

const [s1, s2] = data.reduce((acc, arr) => {
  acc.forEach((s, i) => s.add(arr[i + 1]))

  return acc
}, [new Set(), new Set()])

const Arr1 = [...s1]
const Arr2 = [...s2]

console.log({ Arr1, Arr2 })

Comments

1

You could take a single loop and get the unique values for all items of the nested arrays.

const
    getUnique = array => array
        .reduce((r, a) => a.map((v, i) => (r[i] || new Set).add(v)), [])
        .map(s => [...s]),
    data = [["Apple", "Fruit", "Red"], ["Pear", "Fruit", "Green"], ["Orange", "Fruit", "Orange"], ["Carrot", "Vegetable", "Orange"], ["Pea", "Vegetable", "Green"], ["Pumpkin", "Vegetable", "Orange"]],
    [, types, colors] = getUnique(data);
    
console.log(types);
console.log(colors);

Comments

1

Create an object with foods and colors as keys and loop through inserting the appropriate element index each iteration

let data = [
  ["Apple","Fruit","Red"],
  ["Pear","Fruit","Green"],
  ["Orange","Fruit","Orange"],
  ["Carrot","Vegetable","Orange"],
  ["Pea","Vegetable","Green"],
  ["Pumpkin","Vegetable","Orange"]
]

const uniques = {
   foods:new Set(),
   colors: new Set()

}
data.forEach(a => uniques.foods.add(a[1]) && uniques.colors.add(a[2]) )

// just for demo logs
Object.entries(uniques).forEach(([k,set])=> console.log(k,':', ...set))

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.