2

So my question is if i have an array of objects

var arr = [{"id":1, "image":"sport.jpg", "tag_name":"Sport","tag_type":"Takmicenje","my_feed":true, "my_favourites":true},
{"id":2, "image":"sport.jpg", "tag_name":"Fudbal","tag_type":"Takmicenje","my_feed":true, "my_favourites":true},
{"id":3,  "image":"sport.jpg","tag_name":"Premier League","tag_type":"Takmicenje","my_feed":false,"my_favourites":true},
{"id":4, "image":"sport.jpg", "tag_name":"La liga","tag_type":"Takmicenje","my_feed":true,"my_favourites":true},
{"id":5, "image":"sport.jpg", "tag_name":"Real Madrid","tag_type":"Fudabal","my_feed":true,"my_favourites":true},
{"id":6, "image":"sport.jpg","tag_name":"UEFA","tag_type":"Tim","my_feed":true,"my_favourites":true},
{"id":7,"image":"sport.jpg","tag_name":"Juve","tag_type":"Liga","my_feed":true,"my_favourites":false},
{"id":8,"image":"sport.jpg","tag_name":"Barca","tag_type":"Takmicenje","my_feed":false,"my_favourites":true},
{"id":9,"image":"sport.jpg","tag_name":"Sport","tag_type":"Fudbal","my_feed":true,"my_favourites":true}
]

I map trough array and then filter it for removing dupes,(tag_type) but just can't wrap my head around it.

var filtred = arr.map(function(item) { return item.tag_type;
  }).filter(function(filt,index) {
    return filt.indexOf(filt) == index;
});

result of filter is ["Takmicenje"]

map is working correctly

["Takmicenje", "Takmicenje", "Takmicenje", "Takmicenje", "Fudabal", "Tim", "Liga", "Takmicenje", "Fudbal"]

result of filter is

["Takmicenje"]

I want to get array of

['Takmicenje','Fudbal','Tim','Liga'];
4
  • 1
    "can't wrap my head around it." That's not a question. What is your question? Commented May 26, 2017 at 21:44
  • Is the code doing what you want? I'm not sure what the question is. Commented May 26, 2017 at 21:45
  • filt.indexOf(filt) doesn't make a lot of sense in that context Commented May 26, 2017 at 21:49
  • I have edited my question Commented May 26, 2017 at 21:49

5 Answers 5

3

Use filter in the right way:

var arr = [{"id":1, "image":"sport.jpg", "tag_name":"Sport","tag_type":"Takmicenje","my_feed":true, "my_favourites":true},
{"id":2, "image":"sport.jpg", "tag_name":"Fudbal","tag_type":"Takmicenje","my_feed":true, "my_favourites":true},
{"id":3,  "image":"sport.jpg","tag_name":"Premier League","tag_type":"Takmicenje","my_feed":false,"my_favourites":true},
{"id":4, "image":"sport.jpg", "tag_name":"La liga","tag_type":"Takmicenje","my_feed":true,"my_favourites":true},
{"id":5, "image":"sport.jpg", "tag_name":"Real Madrid","tag_type":"Fudabal","my_feed":true,"my_favourites":true},
{"id":6, "image":"sport.jpg","tag_name":"UEFA","tag_type":"Tim","my_feed":true,"my_favourites":true},
{"id":7,"image":"sport.jpg","tag_name":"Juve","tag_type":"Liga","my_feed":true,"my_favourites":false},
{"id":8,"image":"sport.jpg","tag_name":"Barca","tag_type":"Takmicenje","my_feed":false,"my_favourites":true},
{"id":9,"image":"sport.jpg","tag_name":"Sport","tag_type":"Fudbal","my_feed":true,"my_favourites":true}
];

var filtred = arr.map(function(item) { return item.tag_type;
  }).filter(function(filt, ind, currentArray) {
    return ind == currentArray.indexOf(filt);
});
console.log(filtred);

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

Comments

2

The Answers:

Answer #1 -- Pretty & Dynamic:

 let result = [...new Set(arr.map(item=>item.tag_type))];

Answer #2 -- Fast (5X as fast as the top answer)

let tagTypes = {};
arr.forEach(item => tagTypes[item.tag_type] = true)
let result = Object.keys(tagTypes)

The explanation:

Most of the existing answers are perfectly functional, especially for this small of a data-set. The problems begin to arise when you have thousands of items to iterate over, so let’s talk performance. Every response thus far has used Array.prototype.indexOf inside some kind of loop. The problem with this is that indexOf itself is a loop. As a result, @quirimmo’s response iterates 11 times and @SeanGregory’s iterates 10 times.

Below, I will explain how to achieve the desired effect without nesting loops.

Answer #1

Let’s break this apart:

 let result = [...new Set(arr.map(item=>item.tag_type))]

In the center of this, we have the map . This should be familiar.

let allTagTypes = arr.map(item=>item.tag_type)

The ES6 Set is an iterable object which, “lets you store unique values of any type, whether primitive values or object references.” This means when you construct a Set, passing it an array with duplicate items, the resulting Set will essentially remove the duplicates and only store each item once. As such, we can do the following and get a Set of unique tag types:

let filteredTagTypes = new Set(allTagTypes);

This is great, but we want the results as an Array, not a Set. We can convert this Set into an array using the Spread Operator.

let result = [...filteredTagTypes];
// ["Takmicenje","Fudabal","Tim","Liga","Fudbal"]

That’s it!

Notes: The question was about how to filter out duplicates from an array of property values. This solution will work for all types of values. For the asker’s specific scenario (the values always being strings), Answer #2 is considerably better.

As it turns out, the construction of a Set is a relatively heavy process. Performance-wise, we end up in about the same place as with indexOf. It's a one-liner though, so why not? :)

Now for numero dos!

Answer #2

This one is a bit more succinct.

let tagTypes = {};
arr.forEach(item => tagTypes[item.tag_type] = true)
let result = Object.keys(tagTypes)

First, we create an object that will hold our tag types.

let tagTypes = {};

Next, we loop through each item in the array. For each item, we set a property on the tagTypes object with a key of the item’s tag_name and a value of true. If the property is already there, it’s simply overwritten with the same thing. If not, it’s added to the object.

When the loop is complete, tagTypes will be an object that looks like this:

{
     "Takmicenje":true,
     "Fudabal":true,
     "Tim":true,
     "Liga":true,
     "Fudbal":true
}

We can now get an Array of tagTypes' keys using Object.keys().

let result = Object.keys(tagTypes)
// ["Takmicenje","Fudabal","Tim","Liga","Fudbal"]

That’s two iterations. Vroom vroom!

The Results

Here’s a snippet running each function and a JSPerf performance comparison if you’re interested:

JSPerf: https://jsperf.com/unique-array-items-from-properties

let arr = [{"id":1, "image":"sport.jpg", "tag_name":"Sport","tag_type":"Takmicenje","my_feed":true, "my_favourites":true},
{"id":2, "image":"sport.jpg", "tag_name":"Fudbal","tag_type":"Takmicenje","my_feed":true, "my_favourites":true},
{"id":3,  "image":"sport.jpg","tag_name":"Premier League","tag_type":"Takmicenje","my_feed":false,"my_favourites":true},
{"id":4, "image":"sport.jpg", "tag_name":"La liga","tag_type":"Takmicenje","my_feed":true,"my_favourites":true},
{"id":5, "image":"sport.jpg", "tag_name":"Real Madrid","tag_type":"Fudabal","my_feed":true,"my_favourites":true},
{"id":6, "image":"sport.jpg","tag_name":"UEFA","tag_type":"Tim","my_feed":true,"my_favourites":true},
{"id":7,"image":"sport.jpg","tag_name":"Juve","tag_type":"Liga","my_feed":true,"my_favourites":false},
{"id":8,"image":"sport.jpg","tag_name":"Barca","tag_type":"Takmicenje","my_feed":false,"my_favourites":true},
{"id":9,"image":"sport.jpg","tag_name":"Sport","tag_type":"Fudbal","my_feed":true,"my_favourites":true}
];

function withSet(arr){ // Answer #1
	return [...new Set(arr.map(item=>item.tag_type))]
}

function withObject(arr){ // Answer #2
	let tagTypes = {};
	arr.forEach(item => tagTypes[item.tag_type] = true)
	return Object.keys(tagTypes)
}

console.log('With Object', withObject(arr))
console.log('With Set', withSet(arr))

Note: in the question, it says the desired result is ['Takmicenje','Fudbal','Tim','Liga'], but the actual result with duplicates removed is [“Takmicenje", "Fudabal", "Tim", "Liga", "Fudbal"].

2 Comments

This one takes advantage of hashing, it is indeed a faster solution. However the previous best solution was mine (O (n log n) rather than O(n^2)), not sure why you decided to omit it.
@arboreal84 I only included quirimmo's answer as a general example of the performance implications of nesting loops, not because it was the fastest. Either way, the question was about using a filter, so it's totally fair for that answer to be on top
0

It looks like you just want an array of tag_type with duplicates removed. Here you go

var arr = [{"id":1, "image":"sport.jpg", "tag_name":"Sport","tag_type":"Takmicenje","my_feed":true, "my_favourites":true},
{"id":2, "image":"sport.jpg", "tag_name":"Fudbal","tag_type":"Takmicenje","my_feed":true, "my_favourites":true},
{"id":3,  "image":"sport.jpg","tag_name":"Premier League","tag_type":"Takmicenje","my_feed":false,"my_favourites":true},
{"id":4, "image":"sport.jpg", "tag_name":"La liga","tag_type":"Takmicenje","my_feed":true,"my_favourites":true},
{"id":5, "image":"sport.jpg", "tag_name":"Real Madrid","tag_type":"Fudabal","my_feed":true,"my_favourites":true},
{"id":6, "image":"sport.jpg","tag_name":"UEFA","tag_type":"Tim","my_feed":true,"my_favourites":true},
{"id":7,"image":"sport.jpg","tag_name":"Juve","tag_type":"Liga","my_feed":true,"my_favourites":false},
{"id":8,"image":"sport.jpg","tag_name":"Barca","tag_type":"Takmicenje","my_feed":false,"my_favourites":true},
{"id":9,"image":"sport.jpg","tag_name":"Sport","tag_type":"Fudbal","my_feed":true,"my_favourites":true}
];

var filtered = arr.map(function(item) {
                      return item.tag_type;
                  })
                  .reduce(function(result, currentTagType) {
                      if (result.indexOf(currentTagType) === -1)
                          result.push(currentTagType);
                      return result;
                  }, []);

console.log(filtered);

3 Comments

@arboreal84 And your suggestion?
@arboreal84 your solution would perform the worst. Because you first have to sort it, then removing the next items that are equal like you said, removing items in the middle of a list IS expensive. Calm down there bud
@orboreal84 Why don't you post your implementation and see if runs faster than the other answers including mine, if it does, I'll give you a +1 even though I did not give you the downvote
0

Simply use this :

var filtred = arr.map(function(item) { return item.tag_type;
  }).filter(function(filt,index, mapped) { 
    return mapped.indexOf(filt) == index;
});

Enjoy

1 Comment

It's maybe not a problem for this issue, but you're right, store data in a map will work far faster than my answer
-1

https://jsperf.com/unique-array-items-from-properties-2/1

Implemented a recursive solution. Pretty darn fast. and ES6 has O(1) memory complexity for tail end recursion

Recursion with indexOf

function recurse(arr, newArr){
  let el = arr.shift();
  if (el === undefined){
    return newArr;  
  }else{
    if (newArr.indexOf(el.tag_name) === -1){
      newArr.push(el.tag_name);
    }
    recurse(arr, newArr);
  }
}
recurse(arr, []);

Recursion using an object (based on mccallofthewild's answer)

  function withRecursionAndObject(arr, newObj){
    let el = arr.shift();
    if (el === undefined){
      return Object.keys(newObj);  
    }else{
      newObj[el.tag_type] = true;
      withRecursionAndObject(arr, newObj);
    }
  }

Using While

let i = arr.length;
var filtered = [];
while(i--){
  if (filtered.indexOf(arr[i].tag_name) === -1){
    filtered.push(array[i].tag_name);
  }
}

https://jsperf.com/filter-vs-while-vs-lodash-vs-sort

13 Comments

Would a for loop be faster? I have to imagine it would.
Updated my answer.
while iterates over all items. indexOf also iterates over all items in worst case scenario. For an input of n items, time complexity is O(n^2). It's still the same.
whoops. I missed something.
@SeanGregory I think that you got a bit confused. You should add the element if it's not present (so === -1) and if you you have to push just the tag_name not all the object (otherwise your check then doesn't work either). here your jsfiddle working: jsfiddle.net/og8pm1m8
|

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.