优雅的缩小功能

时间:2019-01-31 14:50:49

标签: javascript arrays ecmascript-6

我正在尝试减少对象数组(在我的情况下为配置值)。 我的数组如下所示:

const settings = [
  {room: null, key: 'radioEnabled', value: true},
  {room: 24,   key: 'radioEnabled', value: false},
  {room: 24,   key: 'name',         value: 'Jack'},
  {room: 23,   key: 'name',         value: 'Mike'},
  {room: 23,   key: 'radioEnabled', value: false},
  {room: null, key: 'tvEnabled',    value: false},
];

此数组没有任何排序方式。
如果将房间设置为null,则表示它是全局设置。 全局设置可以被本地设置覆盖。

我正在尝试编写一个函数来获取房间的所有设置。 对于24号房,它应返回:

[
  {room: 24,   key: 'radioEnabled', value: false},
  {room: 24,   key: 'name',         value: 'Jack'},
  {room: null, key: 'tvEnabled',    value: false},
]

返回值的顺序对我来说并不重要。 我已经能够通过多种方式实现这一目标,但是这些解决方案对我而言似乎并不那么优雅/可读。有人可以提出一个更优雅的主意吗?

我的解决方案在jsfiddle下方和上方。

const settings = [
  {room: null, key: 'radioEnabled', value: true},
  {room: 24,   key: 'radioEnabled', value: false},
  {room: 24,   key: 'name',         value: 'Jack'},
  {room: 23,   key: 'name',         value: 'Mike'},
  {room: 23,   key: 'radioEnabled', value: false},
  {room: null, key: 'tvEnabled',    value: false},
];

const getAll_1 = function(room){
	return settings.reduce( (a, b) => {
  	// remove all other rooms
  	if(b.room && b.room!== room){
    	return a;
  	}

		// see if the setting is already added
  	let found = a.find( (setting) => {
    	return setting.key === b.key;
  	})

		// we already have a local value in our return array, don't add/replace anything
  	if( found && found.room === room) {
    	return a;
  	} 
  
  	// we have a value, but it's not local. Replace the current value with the new one
	  if( found ) {
      const index = a.findIndex( (setting) => {
    		return setting.key === b.key;
  		})
  	  a[index] = b;
    	return a;
  	}
  
  	// we don't have this setting at all. add it. 
	  return a.concat(b);
	}, []);
}

const getAll_2 = function(room){
	return settings
    // first filter out all other room settings, only keep current room and global values
  	.filter( (setting) => {
		  return setting.room === null || setting.room === room;
		})
    // than sort em so all local (high prio) values are up top
		.sort( (a, b) => {
		  return (a.room > b.room) ? -1 : ( a.room < b.room ) ? 1 : 0;
		})
    // reduce the array, adding only global values if they are not already added as local value
		.reduce( (a, b) => {
		  const found = a.find( (setting) => {
  		  return setting.key === b.key;
  		})
  		if (found){
  			return a;
  		}
  		return a.concat(b);
		}, [])
}

console.log(`Stack Overflow does not support console.table. Open your console for better representation`);

console.log(`getAll_1 response:`);
console.table(getAll_1(24));
console.log(`getAll_2 response:`);
console.table(getAll_2(24));
Check your console

6 个答案:

答案 0 :(得分:2)

另一种可能会或可能无法满足您的基本要求的方法是将其转换为更有用的格式:

const roomSettings = settings => {
  const globals = settings.filter(s => s.room == null)
    .reduce((all, {key, value}) => ({...all, [key]: value}), {})
  
  return settings.filter(s => s.room != null)
    .reduce((all, {room, key, value}) => ({
      ...all, 
      [room]: {...(all[room] || globals), [key]: value}
    }), {} )
}

const settings = [{"key": "radioEnabled", "room": null, "value": true}, {"key": "radioEnabled", "room": 24, "value": false}, {"key": "name", "room": 24, "value": "Jack"}, {"key": "name", "room": 23, "value": "Mike"}, {"key": "radioEnabled", "room": 23, "value": false}, {"key": "tvEnabled", "room": null, "value": false}, {"key": "name", "room": 25, "value": "Beth"}]

console.log(roomSettings(settings))

请注意,这将返回如下内容:

{
  23: {
    radioEnabled: false,
    tvEnabled: false,
    name: "Mike"
  },
  24: {
    radioEnabled: false,
    tvEnabled: false,
    name: "Jack"
  },
  25: {
    radioEnabled: true,
    tvEnabled: false,
    name: "Beth"
  } 
}

(我加了'Beth',以便至少拥有一个不是false / false的东西。)

这种格式看起来更有用,但肯定不适合您。

答案 1 :(得分:1)

基本上,对数组使用过滤器会更容易,此过滤器函数包装在一个较高的函数中,以接收房间号作为参数。

编辑:忘记了减少删除重复项的操作。

const settings = [{
    room: 24,
    key: 'radioEnabled',
    value: false
  },
  {
    room: null,
    key: 'radioEnabled',
    value: true
  },
  {
    room: 24,
    key: 'name',
    value: 'Jack'
  },
  {
    room: 23,
    key: 'name',
    value: 'Mike'
  },
  {
    room: 23,
    key: 'radioEnabled',
    value: false
  },
  {
    room: null,
    key: 'tvEnabled',
    value: false
  },
];


const getConfigs = (room = null, settings = []) => {
  return settings
    // filter all the options that match our criteria.
    .filter(setting => setting.room === room || setting.room === null)
    // using reduce we will remove the duplicated key entries.
    .reduce((accum, currentVal) => {
      // if the index is -1 it means it is not on the array, so we add it. 
      const index = accum.findIndex(accumSetting => accumSetting.key === currentVal.key)
      if (index === -1) {
        accum.push(currentVal);
      } else { // it means that we have the entry. replace if we have a local one.
        if(currentVal.room === room && accum[index].room === null){
          accum[index] = currentVal;
        }
        
      }
      return accum;
    }, [])
}

const result24 = getConfigs(24, settings);
const result23 = getConfigs(23, settings);

console.log(result24);
console.log(result23);

答案 2 :(得分:1)

这是一个可能的选择-

const globals =
  { radioEnabled: true
  , tvEnabled: false
  }

const settings =
  [ { room: 24, name: 'Jack' }
  , { room: 24, radioEnabled: false }
  , { room: 25, name: 'Mike' }
  , { room: 25, tvEnabled: true }
  ]

const assign = (o1, o2) =>
  ({ ...o1, ...o2 })

const getConfig = (room, settings = []) =>
  settings
    .filter (s => s.room === room)
    .reduce (assign, globals)

console .log
  ( getConfig (24, settings)
    // { radioEnabled: false
    // , tvEnabled: false
    // , room: 24
    // , name: 'Jack'
    // }

  , getConfig (25, settings)
    // { radioEnabled: true
    // , tvEnabled: true
    // , room: 25
    // , name: 'Mike'
    // }
  )

答案 3 :(得分:1)

您可以先获取specific设置,然后再添加general,如果key尚无特定设置:

const settings = [
  {room: null, key: 'radioEnabled', value: true},
  {room: 24,   key: 'radioEnabled', value: false},
  {room: 24,   key: 'name',         value: 'Jack'},
  {room: 23,   key: 'name',         value: 'Mike'},
  {room: 23,   key: 'radioEnabled', value: false},
  {room: null, key: 'tvEnabled',    value: false},
];

const generalSettings = settings.filter(x => x.room === null);

const getSettings = (roomID) =>
{
    let keysAdded = new Set();

    // Get specific settings and add keys on the set.

    let res = settings.filter(x => x.room == roomID && keysAdded.add(x.key));
    
    // Add general settings.

    return generalSettings.reduce(
        (acc, curr) => keysAdded.has(curr.key) ? acc : [...acc, curr],
        res
    );
}


console.log(getSettings(23));
console.log(getSettings(24));

答案 4 :(得分:1)

我建议:

  • 首先过滤room为空的位置
  • 然后过滤room是所需房号的地方
  • 以该顺序连接两个结果
  • 将其添加到Map中,每个键只能获得一个条目
  • 输出为数组

前两个过滤器结果按此顺序串联在一起的事实将确保null个房间条目的优先级低于非空条目。

function getAll(room){
    return [...new Map([...settings.filter(a => a.room === null), 
                        ...settings.filter(a => a.room === room)]
               .map(a => [a.key, a])).values()]
}

const settings = [{room: null, key: 'radioEnabled', value: true},{room: 24,   key: 'radioEnabled', value: false},{room: 24,   key: 'name', value: 'Jack'},{room: 23,   key: 'name',         value: 'Mike'},{room: 23,   key: 'radioEnabled', value: false},{room: null, key: 'tvEnabled', value: false}];

console.log(getAll(24));

答案 5 :(得分:0)

如果将结果存储在基于reduce的对象中,然后获取其值以返回数组作为结果,则可以使用一个key循环来执行此操作。

  • 检查房间是否为null或目标值
  • 检查该设置的key属性是否不存在,或者其房间值是否为null

const settings = [{"room":null,"key":"radioEnabled","value":true},{"room":24,"key":"radioEnabled","value":false},{"room":24,"key":"name","value":"Jack"},{"room":23,"key":"name","value":"Mike"},{"room":23,"key":"radioEnabled","value":false},{"room":null,"key":"tvEnabled","value":false}]

const getAll = room => {
  return Object.values(settings.reduce((r, e) => {
    if (e.room == null || e.room == room) {
      if (!r[e.key] || r[e.key].room == null) r[e.key] = e
    }
    return r;
  }, {}))
}

console.log(getAll(24))