21

Essentially, I want to implement the following:

var categories = [];
var products = // some array of product objects
products.map(function(value) {
   if(categories.indexOf(value.Category === -1)) categories.push(value.Category);
});

As result, categories array contains unique list of product categories.

I feel that there should be a better way to do it, but nothing comes to mind.

If there isn't then probably there is no point to use map() in the first place. I could do as simple as

var categories = [];
var products = // some array of product objects
for (var i = 0; i < products.length; i++) {
   if(categories.indexOf(products[i].Category === -1)) categories.push(products[i].Category);
}

UPDATE for those who insist it's a duplicate of "how to make an array unique" question. I saw that post, and for my situation I don't think it applies. I don't have an array of values that I need to make unique. I have an array of objects and I need to build an array of unique values. The difference might be subtle - but to get to the use case of that topic I would build a non-unique array and then make it unique. Seems even worse than my original solution

Felix
  • 9,248
  • 10
  • 57
  • 89
  • 1
    Possible duplicate of [Unique values in an array](http://stackoverflow.com/questions/1960473/unique-values-in-an-array) – Venkat.R Dec 25 '15 at 20:06
  • please add some sample data. – Nina Scholz Dec 25 '15 at 20:15
  • i'm with @NinaScholz – Venkat.R Dec 25 '15 at 20:15
  • 1
    i think, you misuse `map` for simply iterating. the right method is `forEach`. – Nina Scholz Dec 25 '15 at 20:18
  • Then Possible duplicate of http://stackoverflow.com/questions/15125920/how-to-get-distinct-values-from-an-array-of-objects-in-javascript – Venkat.R Dec 25 '15 at 20:18
  • @NinaScholz - I think you are correct. I started with an assumption that map() will give me the ability to filter out as it goes; but if it doesn't than for or forEach is the way to go. Thanks – Felix Dec 25 '15 at 20:22
  • Seriously?! Downvoting the question *and* downvoting first correct answer?! Alright then... – Felix Dec 25 '15 at 20:35
  • @Felix, if you or Bex had checked that answer you approved you'd see there's an error in it. – Andy Dec 25 '15 at 20:36
  • 1
    @Andy - the value of Bek's answer is pointing out that I can use reduce() to build an array; as opposed to generating a scalar. There were some edits in Bek's answer; so maybe the error was fixed - or maybe I got the idea and implemented it correctly. In any event, it works fine! – Felix Dec 25 '15 at 20:46
  • @Felix But it has no value as an answer to anyone else coming to this question in months/years time if it's full of errors. – Andy Dec 25 '15 at 20:49
  • Andy - somebody else will have somewhat different situation. In fact, my real situation didn't deal with products and categories, either. I tried to simplify the question by removing irrelevant stuff (didn't help me much - still got downvoted for some unclear reason - but hey, it's life). Bek's answer got me in the right direction - and I thank him for that. I wish I could accept @NinaScholz answer as well. In any event, Bek made the correction. Let's all be a little nicer to each other - it's Christmas, after all! :) – Felix Dec 25 '15 at 21:16

4 Answers4

22

you can use reduce instead of map

var products = [{Category:'vegetable', price: 1}, {Category:'fruits', price: 2}];
var categories = products.reduce(function(sum, product) {
 if(sum.indexOf(product.Category) === -1){
  sum.push(product.Category);
 }
 return sum;
}, []);
Bek
  • 3,157
  • 1
  • 17
  • 28
6

Update: A solution with Array.prototype.reduce()

var products = [{ Name: 'milk', price: 2.50, Category: 'groceries' }, { Name: 'shirt', price: 10, Category: 'clothing' }, { Name: 'apples', price: 5, Category: 'groceries' }],
    categories = products.reduce(function (r, a) {
        if (!~r.indexOf(a.Category)) {
            r.push(a.Category);
        }
        return r;
    }, []);

document.write('<pre>' + JSON.stringify(categories, 0, 4) + '</pre>');
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • 1
    I don't think this will work - within `filter` categories is always an empty array, and comparison will **always** return true. Or am I misunderstanding? – Felix Dec 25 '15 at 20:10
  • please supply some example data, the it's more likely to see what you want. – Nina Scholz Dec 25 '15 at 20:11
  • @Felix this answer shows how to implement what question asking (filter values in functional style vs. imperative), but it is not what you actually need and duplicate Vankat found answers it perfectly. – Alexei Levenkov Dec 25 '15 at 20:18
  • 1
    Products = [{Name: 'milk', price: 2.50, Category: 'groceries'}, {Name: 'shirt', price: 10, Category: 'clothing'}, {Name: 'apples', price: 5, Category: 'groceries'}] Expected result: Categories = ['groceries', 'clothing'] – Felix Dec 25 '15 at 20:19
  • Yes - this is exactly what I was looking for. In fairness @Bek answered first. – Felix Dec 25 '15 at 20:30
  • 1
    @Felix, in future, perhaps don't think of it as a race. Bex might have answered first wrt `reduce` but the answer was very poor/not checked. – Andy Dec 25 '15 at 20:55
3

map all the values of the object categories out first, then use filter to dispose of the duplicates.

var products = [
  { category: 'A' },
  { category: 'B' },
  { category: 'A' },
  { category: 'D' }
];

var categories = products.map(function (e) {
  return e.category;
}).filter(function (e, i, a) {
  return a.indexOf(e) === i;
}); // [ "A", "B", "D" ]

DEMO

Andy
  • 61,948
  • 13
  • 68
  • 95
0

Follow the Below SO Answer:

How to get distinct values from an array of objects in JavaScript?

var flags = [], output = [], l = array.length, i;
for( i=0; i<l; i++) {
    if( flags[array[i].age]) continue;
    flags[array[i].age] = true;
    output.push(array[i].age);
}
Community
  • 1
  • 1
Venkat.R
  • 7,420
  • 5
  • 42
  • 63