代码之家  ›  专栏  ›  技术社区  ›  stackoverfloweth

探索大量真/假属性的优雅解决方案

  •  2
  • stackoverfloweth  · 技术社区  · 6 年前

    这是一个非常普遍的问题,但我的特定用例是基于包含true/false设置的对象构建一个“友好”的过滤器名称。

    下面是这些设置对象之一的示例

    var settings = {
        includeMen: true,
        includeWomen: false,
        includeDependents: true,
    }
    

    业余的解决方案是 if 像这样的支架

    if(settings.includeMen && settings.includeWomen && settings.includeDependents){
        return "All People"
    }
    
    if(settings.includeMen && !settings.includeWomen && settings.includeDependents){
        return "Men and Children"
    }
    
    if(settings.includeMen && settings.includeWomen && !settings.includeDependents){
        return "Men and Women"
    }
    ...
    

    有没有业余的想法? (如果有帮助,项目正在使用lodash)

    4 回复  |  直到 6 年前
        1
  •  4
  •   Jonas Wilms    6 年前
     const {includeMen, includeWomen, includeDependents} = settings;
    
     const result = [];
     if(includeMen) result.push("Men");
     if(includeWomen) result.push("Women");
     if(includeDependents) result.push("Children");
    
     if(result.length === 3)
       return "All people";
     if(!result.length)
       return "No one";
    
     return result.join(" and ");
    

    如果你想为每种情况定制输出,你可以做一些有趣的比特转换:

    const {includeMen, includeWomen, includeDependents} = settings;
    
    return [
     "No one",
     "Children",
     "Women",
     "Children and Women",
     "Men",
     "Men and Children",
     "Men and Women",
    "All people"
    ][+includeDependents + (includeWomen << 1) + (includeMen << 2)];
    

    (免责声明:如果你这样做,你的队友会恨你:)

        2
  •  1
  •   maioman    6 年前

    你可以用很多方法, 可能我会在运行时转换对象键,然后生成字符串:

    var settings = {
        includeMen: true,
        includeWomen: false,
        includeDependents: true,
    }
    
    var trueFields = Object.entries(settings).reduce((ac, [k, v]) => 
      v ? 
      [...ac, k.replace(/^include/,'')] :
      ac
    ,[])
    
    var str = !trueFields.length ?
      'nobody ...' :
      trueFields.join(' and ')
    
    
    console.log(str)
        3
  •  0
  •   mittens pair    6 年前

    我在关于转换多个 booleans 带位运算符的to-and-from二进制 Mozilla docs

    自动创建掩码

    可以从一组 布尔值,如下所示:

    function createMask() {
      var nMask = 0,
        nFlag = 0,
        nLen = arguments.length > 32 ? 32 : arguments.length;
      for (nFlag; nFlag < nLen; nMask |= arguments[nFlag] << nFlag++);
      return nMask;
    }
    var mask1 = createMask(true, true, false, true); // 11, i.e.: 1011
    var mask2 = createMask(false, false, true); // 4, i.e.: 0100
    var mask3 = createMask(true); // 1, i.e.: 0001
    // etc.
    
    alert(mask1); // prints 11, i.e.: 1011

    反向算法:来自掩码的布尔数组

    如果要创建 Array 属于 Booleans 从掩码可以使用以下代码:

    function arrayFromMask(nMask) {
      // nMask must be between -2147483648 and 2147483647
      if (nMask > 0x7fffffff || nMask < -0x80000000) { 
        throw new TypeError('arrayFromMask - out of range'); 
      }
      for (var nShifted = nMask, aFromMask = []; nShifted; 
           aFromMask.push(Boolean(nShifted & 1)), nShifted >>>= 1);
      return aFromMask;
    }
    
    var array1 = arrayFromMask(11);
    var array2 = arrayFromMask(4);
    var array3 = arrayFromMask(1);
    
    alert('[' + array1.join(', ') + ']');
    // prints "[true, true, false, true]", i.e.: 11, i.e.: 1011
    
        4
  •  0
  •   Alex Young    5 年前

    你得到的各种答案表明,没有一种事实上正确的方法可以实现这一点。我的方法是创建一个过滤器名称定义数组,该数组既易于读取又易于编辑,并且可以通过搜索查找匹配的名称。

    我喜欢这种方法的原因是,当添加新的过滤器时,使用新的名称,不需要编辑执行搜索的逻辑。只需将新定义添加到筛选器名称列表中。

    这1)减少了引入错误的机会,2)将复杂度封装在一个易于测试和很少改变的函数中,3)对团队成员来说是显而易见的。

    const SETTING_PREFIX = "include";
    
    const removeInclude = k => k.substr(SETTING_PREFIX.length, k.length);
    
    const isTrue = x => !!x;
    const isFalse = x => !x;
    
    const getFilteredProps = (filter, predicate) => {
      return Object.keys(filter).filter(k =>
        predicate(filter[k])
      )
    }
    
    const getFilterName = filter => {
      const trueFilterProps = getFilteredProps(filter, isTrue).map(removeInclude);
      const falseFilterProps = getFilteredProps(filter, isFalse).map(removeInclude);;
      
      const filterNameDefinition = filterNames.find(definition => 
        trueFilterProps.every(key =>
            definition[0].includes(key)
        ) &&
        falseFilterProps.every(key =>
            !definition[0].includes(key)
        )
      );
      
      if (!filterNameDefinition) {
        return "Unnamed filter";
      }
      
      return filterNameDefinition[0].join(" and ");
    }
    
    const settings = {
      includeMen: false,
      includeWomen: true,
      includeDependents: true
    };
    
    const filterNames = [
      [["Men", "Women", "Dependents"], "All People"],
      [["Men", "Dependents"], "Men and Children"]
    ];
    
    console.log(getFilterName(settings))