1

I have a javascript object below:

let filters = {
     numbers: {
            '0': 'is_no_selected',
            '1': 'is_one_selected',
            '2': 'is_two_selected',
            '3': 'is_three_selected',
            '4': 'is_four_selected',
            '5': 'is_five_selected',
          },
     words: {
            please: 'is_please_selected',
            help: 'is_help_selected',
            me: 'is_me_selected',
     },
}

And I got two input:'numbers' and ['4','5']

I want to get:

{
            numbers: {
                'is_no_selected': false,
                'is_one_selected': false,
                'is_two_selected': false,
                'is_three_selected': false,
                'is_four_selected': true,
                'is_five_selected': true,
            }
}

And when I input other two input:'words' and ['please']

I want to get:

{
            words: {
                'is_please_selected': true,
                'is_help_selected': false,
                'is_me_selected': false,
            }
}

How to make this happen using reduce()?

1
  • in general, I guess you could convert object to Array then reduce Commented Aug 13, 2021 at 2:28

9 Answers 9

1

You need to get the entries of the object before using reduce:

let filters = {
     numbers: {
            '0': 'is_no_selected',
            '1': 'is_one_selected',
            '2': 'is_two_selected',
            '3': 'is_three_selected',
            '4': 'is_four_selected',
            '5': 'is_five_selected',
          },
     words: {
            please: 'is_please_selected',
            help: 'is_help_selected',
            me: 'is_me_selected',
     },
}

function fn(obj, filterKey, filters) {
  filters = filters.map(x => ''+x); // convert all filters to strings (keys are always string)
  return Object
    .keys(obj)
    .filter(x => x === filterKey) // only the selected property
    .map(x => obj[x]) // get the object
    .map(x => {
      return Object.entries(x).reduce((out, curr) => {
        const [key, val] = curr;
        out[val] = filters.includes(key);
        return out;
      }, {})
    });
}

console.log(fn(filters, 'numbers', [0]))

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

Comments

1

I'd do something like below

const process = function (data, key, input) {
     const result = Object.keys(data[key])
        .reduce((acc, index) => {
            const isValid = input.includes(index);
            if(isValid)
                acc[data[key][index]] = true;
            else 
                acc[data[key][index]] = false;
            return acc;
        }, {});

    return {
        [key] : result
    }
};

var filters = {
     numbers: {
            '0': 'is_no_selected',
            '1': 'is_one_selected',
            '2': 'is_two_selected',
            '3': 'is_three_selected',
            '4': 'is_four_selected',
            '5': 'is_five_selected',
          },
     words: {
            please: 'is_please_selected',
            help: 'is_help_selected',
            me: 'is_me_selected',
     },
};


// client code: process(filters, 'numbers', ['4', '5']));

Comments

1

For me the simplest is to combine the inputs into an object with the same numbers and words keys.

Then iterate the Object.entries() and match the numbers/words keys between input and filter objects

let filters={numbers:{0:"is_no_selected",1:"is_one_selected",2:"is_two_selected",3:"is_three_selected",4:"is_four_selected",5:"is_five_selected"},words:{please:"is_please_selected",help:"is_help_selected",me:"is_me_selected"}};

const inputs = {
  numbers: ['4', '5'],
  words: ['please'],
};

const res = {};

Object.entries(filters).forEach(([key,o])=>{
   const fil =  res[key] = {};  
   Object.entries(o).forEach(([k,v]) => fil[v] = inputs[key].includes(k));
})

console.log(res)

Comments

1

Here you go. Not how I would have done it, but if you must use reduce, you can do it like this.

let filters = {
  numbers: {
    '0': 'is_no_selected',
    '1': 'is_one_selected',
    '2': 'is_two_selected',
    '3': 'is_three_selected',
    '4': 'is_four_selected',
    '5': 'is_five_selected',
  },
  words: {
    please: 'is_please_selected',
    help: 'is_help_selected',
    me: 'is_me_selected',
  },
}

var numbers = filter(filters, 'numbers', ['4', '5']);
var words = filter(filters, 'words', ['please']);

console.log(numbers);
console.log(words);

function filter(filter_obj, key, input) {
  return {
    [key]: Object.keys(filters[key]).reduce((a, c) => {
      a[filters[key][c]] = input.includes(c)
      return a;
    }, {})
  }
}

Comments

1

Using two reduce() for filtering the inner object and the outer object.

/* input is the data and filtering key and value list */
function filter (data, key, vals) {

  // inner filter
  const filterValues = (obj, values) => Object.keys(obj).reduce((accum, ele) => {
    accum[obj[ele]] = values.includes(ele);
    return accum;
  }, {});

  // outer filter
  const filteredKey = Object.keys(data).reduce((accum, ele) => {
    if (ele === key)
      accum[key] = data[key];
    return accum;
  }, {});

  // call outer filter first then pass it to inner filter
  return {
    [key]: filterValues(filteredKey[key], vals)
  };
}

Call it with the data you gave in the firs argument as:

console.log(filter(data, 'numbers', ['4', '5']));
console.log(filter(data, 'words', ['please']));

Here is the complete code you can test and run.

const data = {
  numbers: {
    '0': 'is_no_selected',
    '1': 'is_one_selected',
    '2': 'is_two_selected',
    '3': 'is_three_selected',
    '4': 'is_four_selected',
    '5': 'is_five_selected',
  },
  words: {
    please: 'is_please_selected',
    help: 'is_help_selected',
    me: 'is_me_selected',
  },
}

/* input is data and filtering key and value list */
function filter (data, key, vals) {
  // inner filter
  const filterValues = (obj, values) => Object.keys(obj).reduce((accum, ele) => {
    accum[obj[ele]] = values.includes(ele);
    return accum;
  }, {});
  // outer filter
  const filteredKey = Object.keys(data).reduce((accum, ele) => {
    if (ele === key)
      accum[key] = data[key];
    return accum;
  }, {});
  // call outer filter first then pass it to inner filter
  return {
    [key]: filterValues(filteredKey[key], vals)
  };
}

console.log(filter(data, 'numbers', ['4', '5']));
console.log(filter(data, 'words', ['please']));

Comments

1

Sure, very simple one line code

let filters = {
     numbers: {
            '0': 'is_no_selected',
            '1': 'is_one_selected',
            '2': 'is_two_selected',
            '3': 'is_three_selected',
            '4': 'is_four_selected',
            '5': 'is_five_selected',
          },
     words: {
            please: 'is_please_selected',
            help: 'is_help_selected',
            me: 'is_me_selected',
     },
}
const fn=(f,a)=>({[f]:Object.entries(filters[f]).reduce((r,[k,v])=>(r[v]=a.includes(k),r),{})});

console.log(fn('numbers', ['4','5']))
console.log(fn('words',['please']))

Comments

1

A Basic solution:

function filter(all_option, select_key, select_option) {
    let ret = {};
    let s_f = all_option[select_key];
    for (let k in s_f) {
        if (select_option.includes(k)) {
            ret[s_f[k]] = true;
        } else {
            ret[s_f[k]] = false;
        }
    }
    return ret;
}

convert it to map reduce version:

function filter(all_option, select_key, select_option) {
    let ret = {};
    let s_f = all_option[select_key];
    Object.keys(s_f).map(k => {
        if (select_option.includes(k)) {
            ret[s_f[k]] = true;
        } else {
            ret[s_f[k]] = false;
        }
    });
    return ret;
}

Comments

1

this way...

const filters = 
  { numbers: 
    { '0': 'is_no_selected'
    , '1': 'is_one_selected'
    , '2': 'is_two_selected'
    , '3': 'is_three_selected'
    , '4': 'is_four_selected'
    , '5': 'is_five_selected'
    } 
  , words: 
    { please: 'is_please_selected'
    , help: 'is_help_selected'
    , me: 'is_me_selected'
    } 
  } 

const foo = (in1,in2) =>
  Object.entries(filters[ in1 ])
        .reduce((r,[k,v]) => Object.assign(r,{[v]:in2.includes(k)}), {})


console.log('a;', foo('numbers', ['4','5'])  )
console.log('b;', foo('words',   ['please']) )
.as-console-wrapper { max-height: 100% !important; top: 0 }

Comments

1

You can reduce your numbers and words objects by reducing their Object.entries() which returns an array of an object's own enumerable property [key, value] pairs.

let filters = {
     numbers: {
            '0': 'is_no_selected',
            '1': 'is_one_selected',
            '2': 'is_two_selected',
            '3': 'is_three_selected',
            '4': 'is_four_selected',
            '5': 'is_five_selected',
          },
     words: {
            please: 'is_please_selected',
            help: 'is_help_selected',
            me: 'is_me_selected',
     },
};

let reduceFilter = function(filters, name, input) {
  return Object.entries(filters[name]).reduce((acc, [key, value]) => {
    acc[name][value] = input.includes(key);
    return acc;
  }, {[name]: {}}); 
};

console.log(reduceFilter(filters, 'numbers', ['4', '5']));
console.log(reduceFilter(filters, 'words', ['please']));

Notice that both the reducer function expressions make use of destructing assignments above to break apart the [key, value] pairs easily.

3 Comments

why the hard coded filter property and two identical functions?
@Bravo Thank you for your constructive critique. I request you to review my answer again.
now it looks just like half of all the other answers - so, yeah, that's wonderful

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.