编辑JSON对象 - 寻找更好的功能性approch

时间:2015-10-25 10:13:03

标签: javascript functional-programming

我有一个带有设置的JSON对象,类似于:

var settings = {
  tab1: {
    active: true,
    selectOrder: 1,
    selected: false
  },
  tab2: {
    active: true,
    selectOrder: 2,
    selected: false
  },
  tab3: {
    active: false,
    selectOrder: 0,
    selected: false
  }
};

现在,我的目标是设置选择的'财产是真实的'对于具有最低选择订单的活动元素'在这个例子中,我应该设置选择'对于' tab1' element(' tab3' selectedOrder为0但未激活)。

背后的原因是根据优先级选择第一个活动选项卡(selectOrder)。

我的解决方案是:

function selectFirstActiveTab(visibilitySettings) {
  var selected = 1000;
  var answer = jQuery.extend({}, visibilitySettings);
  Object.keys(answer).forEach((key) => {
    if (answer[key].active && answer[key].selectOrder <= selected) {
      selected = answer[key].selectOrder;
    }
  });

  Object.keys(answer).forEach((key) => {
    answer[key].selected = (answer[key].selectOrder === selected) ? true : false;
  });
  return answer;
};

虽然这个解决方案正在运行,但它似乎很糟糕。

我的问题是:

  1. 是否有更好的&#34;功能性编程&#34;方法呢? (例如,使用map / reduce等)

  2. 我觉得我的设置对象结构不合理。也许我应该选择一个对象数组而不是这个JSON?选择像这样的JSON的原因是为了更简单地绑定到我的模板(类似于&#34; {{settins.tab1.selected}}&#34; - 在我的html中查找它更简单)。这里有什么更好的方法?

  3. Here is the complete example in JSBIN

3 个答案:

答案 0 :(得分:2)

  1. 对于Object上的map和reduce,您可以使用Underscore
  2. function selectFirstActiveTab(visibilitySettings) {
      // Clone input, with selected set to false.
      var answer = _.mapObject(visibilitySettings, (item, key) => {
        return {active: item.active, selectOrder: item.selectOrder, selected: false};
      });
    
      // Find the min among all value.
      var minObj = _.reduce(answer, (min, item) => {
        return (item.active && item.selectOrder < min.selectOrder) ? item : min;
      }, {active: true, selectOrder: Number.MAX_SAFE_INTEGER});
    
      // Set min to selected.
      minObj.selected = true;
      return answer;
    };
    

    jsBin

    或vanilla javascript:

    function selectFirstActiveTab(visibilitySettings) {
      var keys = Object.keys(visibilitySettings);
    
      // Create a cloned obj, like map
      var answer = {};
      keys.forEach((key) => {
        var cloneTarget = visibilitySettings[key];
        answer[key] = {
          active: cloneTarget.active,
          selectOrder: cloneTarget.selectOrder,
          selected: false
        };
      });
    
      // Convert key => value map to an array of value, then reduce it to find min.
      var minimun = keys.map((key) => answer[key]).reduce((min, item) => {
        return (item.active && item.selectOrder < min.selectOrder) ? item : min;
      }, {active: true, selectOrder: Number.MAX_SAFE_INTEGER});
    
      // Set min to selected.
      minimun.selected = true;
      return answer;
    };
    

    jsBin

    1. 它不是JSON,它只是一个带有一些键值映射的普通Object,我们将它用作Map。如果输入是如下数组,则可能更容易应用上面的逻辑:
    2. var settings = [
        {
          name: 'tab1',
          active: true,
          selectOrder: 1,
          selected: false
        },
        {
          name: 'tab2',
          active: true,
          selectOrder: 2,
          selected: false
        },
        {
          name: 'tab3',
          active: false,
          selectOrder: 0,
          selected: false
        }
      ];
      

      由于vanilla javascript支持array上的reduce / map。但是,像lodashunderscore这样的许多库只会使对象上的reduce / map可用。您可以 只需选择最适合您需求的那个(因为那里有很多类似的库,可以让你通过对象进行映射/缩小)。

答案 1 :(得分:2)

使用lodash,您可以编写以下内容:

import { chain, transform, extend } from 'lodash';

transformObj = (obj) => chain(settings)
  .map((obj, key)=> ({ key, obj }))
  .select(({ obj: { active }})=> active)
  .min(({ obj: { selectOrder }})=> selectOrder)
  .thru(({ key })=> transform(settings, (memo, val, _key) => {
      memo[_key] = extend({}, val, {
        selected: (_key == key)
      })
    }, {})
  )
  .value()
> transformObj(settings)
{ tab1: { active: true, selectOrder: 1, selected: true },
  tab2: { active: true, selectOrder: 2, selected: false },
  tab3: { active: false, selectOrder: 0, selected: false } }

以上假定ES6 importsarrow functions和参数destructuring的可用性。否则,您可以使用babel

这更具功能性,因为transformObj是引用透明的&amp;不会改变它的论点。

答案 2 :(得分:0)

试试这个:

tmp = 0;
_tab = false;
for ( tab in settings ){
    if((settings[tab].active)){
        if(!tmp){
            tmp = settings[tab].selectOrder;
            _tab  = tab;
        }else if(settings[tab].selectOrder < tmp){
            _tab = tab;
        }
    };
}
settings[_tab].selected = true;