Элегантная функция уменьшения - PullRequest
0 голосов
/ 31 января 2019

Я пытаюсь уменьшить массив объектов (значения конфигурации в моем случае).Мой массив выглядит так:

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 голосов
/ 31 января 2019

Другой подход, который может или не может помочь с вашими основными требованиями, заключается в преобразовании этого в более полезный формат:

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"
  } 
}

(я добавил «Бет», чтобы был хотя бы один, который не был false / false.)

Этот формат выглядит более полезным, но, безусловно, может быть не для вас.

0 голосов
/ 31 января 2019

Я бы предложил:

  • Сначала отфильтровать, где 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));
0 голосов
/ 31 января 2019

Сначала вы можете получить настройки 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));
0 голосов
/ 31 января 2019

Вот возможная альтернатива -

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'
    // }
  )
0 голосов
/ 31 января 2019

Вы можете сделать это с помощью одного цикла 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))
0 голосов
/ 31 января 2019

в основном проще использовать фильтр по вашему массиву, эта функция фильтра обернута в более высокую функцию для получения номера комнаты в качестве параметра.

edit: забыл про уменьшение, чтобы удалить дубликаты.

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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...