How to filter values corresponding to specific keys in an object?

I need to separate out the values in an object by category, which is visible from the key names of the object. Here is part of what the object may look like:

const values = {
        RM1: 'Rarely',
        RM2: 'Often',
        RM3: 'Sometimes',
        RM4: 'Consistently',
        CM1: 'Rarely',
        CM2: 'Never',
        CM3: 'Sometimes',
        CM4: 'Sometimes',
        CO1: 'Often',
        CO2: 'Often',
        CO3: 'Often',
        CO4: 'Sometimes'
}

The categories are RM, CM, CO, etc. I wrote a function that will filter out the categories (answers is a larger object that values is a part of):

const categorizeAnswers = (answers) =>{
  const valueNames = Object.keys(answers.values);
  const values = Object.values(answers.values);
  const getCategoryList = category => valueNames.filter(value => {
    return value.includes(category);
  })
  const RM = getCategoryList("RM");
  const CM = getCategoryList("CM");
  const CO = getCategoryList("CO");
}

But now I'm not sure how to get the values that correspond to those categories. So for RM, I want an array like: ['Rarely', 'Often', 'Sometimes', 'Consistently']. And likewise for the other categories.

Any ideas? I feel like this is really quite simple and I'm missing something obvious.

Upvotes: 1

Views: 68

Answers (6)

RobG
RobG

Reputation: 147363

You could generate the categories dynamically based on the non–digit part of the property name, then add them to an initially empty object all in one reduce call. That way the categories aren't hard coded (but need to fit the same naming pattern):

var values = {
        RM1: 'Rarely',
        RM2: 'Often',
        RM3: 'Sometimes',
        RM4: 'Consistently',
        CM1: 'Rarely',
        CM2: 'Never',
        CM3: 'Sometimes',
        CM4: 'Sometimes',
        CO1: 'Often',
        CO2: 'Often',
        CO3: 'Often',
        CO4: 'Sometimes'
}

function getCategories(data) {
  return Object.keys(data).reduce(function(acc, key){
    var p = key.replace(/\d+/,'');
    acc[p]? acc[p].push(data[key]) : acc[p] = [data[key]];
    return acc;
  }, Object.create(null));
}

console.log(getCategories(values));

Upvotes: 1

Shahbaz Shueb
Shahbaz Shueb

Reputation: 420

You can simply use map on the array of categories to get their values in an array like this:

const RM = getCategoryList("RM").map((key) => answers.values[key]);

Upvotes: 2

Adarsh
Adarsh

Reputation: 827

Here's a much compact solution using ES5.

const getCategoryList = (category) => {
  const categories = [].concat(Object.keys(answers.values).filter((value) => value.includes(category)));
  const values = [].concat(categories.map((value) => answers.values[value]));
  return [categories, values]
}

const [ categories, values ] = getCategoryList('RM');
console.log(categories, values)

Upvotes: 0

brk
brk

Reputation: 50291

Use for..in to iterate the object & indexOf to check if the key name has the passed string

var values = {
  RM1: 'Rarely',
  RM2: 'Often',
  RM3: 'Sometimes',
  RM4: 'Consistently',
  CM1: 'Rarely',
  CM2: 'Never',
  CM3: 'Sometimes',
  CM4: 'Sometimes',
  CO1: 'Often',
  CO2: 'Often',
  CO3: 'Often',
  CO4: 'Sometimes'
}

function getValues(keyName) {
  var valArray = [];
  for (let keys in values) {
    if (keys.indexOf(keyName) !== -1 && valArray.indexOf(values[keys]) === -1) {
      valArray.push(values[keys])

    }

  }
  return valArray;
}
console.log(getValues('CO'))

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386540

You could check with String#startsWith and use a Set for uniuque values

const getCategoryList = category =>
        [...new Set(valuesNames.filter(v => v.startsWith(category))];

Upvotes: 0

gurvinder372
gurvinder372

Reputation: 68383

Any ideas? I feel like this is really quite simple and I'm missing something obvious.

You are using a wrong variable valuesNames, make it valueNames which you had initialized in the same method before

var categorizeAnswers = (answers) =>{
  const valueNames = Object.keys(answers.values);
  const values = Object.values(answers.values);
  const getCategoryList = category => valueNames.filter(value => { //notice the change from valuesNames to valueNames 
    return value.includes(category);
  })

  const RM = getCategoryList("RM");
  console.log(RM);
  const CM = getCategoryList("CM");
  const CO = getCategoryList("CO");
}

Also, Just like "RM1".includes("RM") is true, "ARM1".includes("RM") is also true

Use indexOf

  const getCategoryList = category => valueNames.filter(value => { 
    return value.indexOf(category) == 0;
  })

Upvotes: 0

Related Questions