Reputation: 911
I have the following markup:
<li data-group="bottoms">Pants</li>
<li data-group="bottoms">Pants</li>
<li data-group="bottoms">Pants</li>
<li data-group="tops">Shirt</li>
<li data-group="tops">T-shirt</li>
<li data-group="tops">Polo</li>
<li data-group="accessories">Sunglasses</li>
Im trying to return a list of the unique data-group
names, but I only wanna return it once. Which means from the list above I would like to return a variable that will contain:
My approach to this is to first find the length of the list and then make a for loop which loops over all the elements and prints their data-group
value. After that I would have to do some sorting so that the bottoms and tops dont show up 3 times, but only once.
I have tried to write the initial code which looks like this.
var uniqueCategoryGroupNames = document.querySelectorAll('li[data-group]');
for (var k = 0; k < uniqueCategoryGroupNames.length; k++) {
console.log([k]);
uniqueCategoryGroupNames[k].getAttribute('data-group');
}
Upvotes: 2
Views: 196
Reputation: 5400
The best way is to use new Set
object (which stores only unique values) or use old plain object. Also you can use dataset
if you support browsers IE11+ (otherwise just replace dataset
with getAttribute('data-group')
)
Using Set
var lis = document.querySelectorAll('li[data-group]');
var uniqueGroupsSet = Array.from(lis).reduce((all, li) => all.add(li.dataset.group), new Set());
var uniqueGroupNames = Array.from(uniqueGroupsSet);
Using plain object
var lis = document.querySelectorAll('li[data-group]');
var uniqueGroupsSet = Array.from(lis).reduce((all, li) => {
all[li.dataset.group] = true
return all
}, {});
var uniqueGroupNames = Object.keys(uniqueGroupsSet);
Combined way: plain object + array
var lis = document.querySelectorAll('li[data-group]');
var uniqueGroupNames = []
Array.from(lis).reduce((all, li) => {
if (!all[li.dataset.group]) {
all[li.dataset.group] = true
uniqueGroupNames.push(li.dataset.group)
}
return all
}, {});
console.log(uniqueGroupNames)
Upvotes: 2
Reputation: 12880
You can use a combination of Array#from
, Array#reduce
and Array#includes
:
liArray.reduce( //Go through your Array
(acc, curr) =>
acc.includes(curr.getAttribute('data-group')) ? // Is the current value already stored?
acc // Then don't add the current value
: acc.concat([curr.getAttribute('data-group')]) // Else add the value
, [] // Starting value for the accumulator
);
Demo:
let liArray = Array.from(document.querySelectorAll('li[data-group]'));
let result = liArray.reduce((acc, curr) => acc.includes(curr.getAttribute('data-group')) ? acc : acc.concat([curr.getAttribute('data-group')]), []);
console.log(result);
<ul>
<li data-group="bottoms">Pants</li>
<li data-group="bottoms">Pants</li>
<li data-group="bottoms">Pants</li>
<li data-group="tops">Shirt</li>
<li data-group="tops">T-shirt</li>
<li data-group="tops">Polo</li>
<li data-group="accessories">Sunglasses</li>
</ul>
Upvotes: 3
Reputation: 30739
You can declare a array to store that attributes and check for the existence of the attribute before pushing it into the array:
var uniqueCategoryGroupNames = document.querySelectorAll('li[data-group]');
var res = [];
for (var k = 0; k < uniqueCategoryGroupNames.length; k++) {
var attr = uniqueCategoryGroupNames[k].getAttribute('data-group');
//check that the attribute do not exist in the array
if(res.indexOf(attr) === -1) {
res.push(attr);
}
}
console.log(res);
<li data-group="bottoms">Pants</li>
<li data-group="bottoms">Pants</li>
<li data-group="bottoms">Pants</li>
<li data-group="tops">Shirt</li>
<li data-group="tops">T-shirt</li>
<li data-group="tops">Polo</li>
<li data-group="accessories">Sunglasses</li>
Upvotes: 2