Sackadelic
Sackadelic

Reputation: 1025

How would I simplify this Array Filter method?

I am trying to select a group of radio buttons on a page. Each of the inputs have the following:

<input type="radio" class="wc-pao-addon-field wc-pao-addon-radio" name="addon-761-soapbar-covers-0[]" data-raw-price="32" data-price="32" data-price-type="quantity_based" value="gold-soapbar-set" data-label="Gold Soapbar (Set)">

I can't select by the data attributes as they are shared with other inputs that I don't want to disable. So far, I can only grab the value as the value changes from gold to raw-nickel to polished-nickel to chrome, etc.

What I have is the following:

//Initialize empty array
   this.metalCovers = [];

//Grab all inputs
    this.covers = Array.from(this.soapbarCovers.querySelectorAll("input"));

//Then filter them by their values and store in new variable
    this.chromeCovers = this.covers.filter((cover) => cover.value.indexOf("chrome") !== -1);
    this.blackCovers = this.covers.filter((cover) => cover.value.indexOf("matte") !== -1);
    this.goldCovers = this.covers.filter((cover) => cover.value.indexOf("gold") !== -1);
    this.nickelCovers = this.covers.filter((cover) => cover.value.indexOf("nickel") !== -1);

//Create new array from filtered inputs
    this.metalCovers = [this.chromeCovers, this.blackCovers, this.goldCovers, this.nickelCovers];

//flatten the new array
    this.metalCovers = [].concat.apply([], this.metalCovers);

So, this works, but I'm trying to discover a cleaner way to approach this code. I was thinking it would clean it up if I could Array.filter() by multiple values, but nothing came up in my research.

Does anything stick out to you?

Upvotes: 0

Views: 65

Answers (3)

Marius Augenstein
Marius Augenstein

Reputation: 11

Coming across with this one:

const filtered = Array.from(this.soapbarCovers.querySelectorAll("input"))
    .filter(input => ['chrome', 'matte', 'gold', 'nickel'].includes(input.value));

Does pretty much what you need in just one line.

Example:

const metalCovers = [
    { attr: 'a1', value: 'chrome'},
    { attr: 'a2', value: 'matte'},
    { attr: 'a3', value: 'gold'},
    { attr: 'a4', value: 'nickel'},
    { attr: 'a5', value: 'chrome'},
    { attr: 'a6', value: 'silver'}, // should be left out
    { attr: 'a7', value: 'copper'}, // should be left out
    { attr: 'a8', value: 'gold'},
    { attr: 'a9', value: 'chrome'},
    { attr: 'a10', value: 'matte'},
    { attr: 'a11', value: 'nickel'},
];

const filtered = metalCovers.filter(input => ['chrome', 'matte', 'gold', 'nickel'].includes(input.value));

console.log(filtered);

will output this:

[
  { attr: 'a1', value: 'chrome' },
  { attr: 'a2', value: 'matte' },
  { attr: 'a3', value: 'gold' },
  { attr: 'a4', value: 'nickel' },
  { attr: 'a5', value: 'chrome' },
  { attr: 'a8', value: 'gold' },
  { attr: 'a9', value: 'chrome' },
  { attr: 'a10', value: 'matte' },
  { attr: 'a11', value: 'nickel' }
]

Upvotes: 0

SoulKa
SoulKa

Reputation: 195

//Initialize empty array
this.metalCovers = [];

//Grab all inputs
this.covers = Array.from(this.soapbarCovers.querySelectorAll("input"));

//Then filter them by their values and store in new variable
this.chromeCovers = [];
this.blackCovers = [];
this.goldCovers = [];
this.nickelCovers = [];
this.covers.forEach( cover => {
    switch (cover.value) {
        case "chrome": this.chromeCovers.push(cover); break;
        case "matte": this.blackCovers.push(cover); break;
        case "gold": this.goldCovers.push(cover); break;
        case "raw-nickel":
        case "polished-nickel":
            this.nickelCovers.push(cover);
        break;
        default: console.error(`Invalid cover "${cover.value}"!`);
    }
});

//Create new array from filtered inputs
this.metalCovers = [this.chromeCovers, this.blackCovers, this.goldCovers, this.nickelCovers];

//flatten the new array
this.metalCovers = [].concat.apply([], this.metalCovers);

Upvotes: 1

AKX
AKX

Reputation: 169407

I'd just do something like

// Define the keywords we're looking for in the value
const keywords = ["chrome", "matte", "gold", "nickel"];
// Grab all inputs
const inputs = Array.from(this.soapbarCovers.querySelectorAll("input"));
// Filter them...
this.metalCovers = inputs.filter((cover) => 
  // If any of the keywords appears in the value, 
  // this is an input we care about
  keywords.some((kw) => cover.value.includes(kw))
);

Or if you want to be really efficient and have the browser CSS engine do all the work,

this.metalCovers = Array.from(this.soapbarCovers.querySelectorAll("input[value*=chrome],input[value*=matte],input[value*=gold],input[value*=nickel]"));

which of course can be made more maintainable via

const keywords = ["chrome", "matte", "gold", "nickel"];
this.metalCovers = Array.from(this.soapbarCovers.querySelectorAll(keywords.map(k => `input[value*=${k}`]).join(','));

Upvotes: 2

Related Questions