Перемещение карты ключевого значения объекта

Я реализовал функции, которые позволят мне изменить значение «Карта» «один ко многим», то есть простой объект JavaScript.

Например,

var map = {'foo': 'bar', 'baz': 'bar', 'moo': 'zoo'};

становится

var reversedMap = {'zoo': ['moo'], 'bar': ['foo', 'baz']}

, а также функцию возврата обратного отображения к необменяемому отображению.

Вот jsbin, чтобы показать, что мое решение работает .

В следующем коде я использовал утилиту lo-dash tool tool, , но вам не нужно знакомиться с ней, чтобы понять код . Предположим, что я верю в производительность lo-dash, и то, что я пытаюсь оптимизировать, - это мой собственный код.

Для большей ясности _.forOwn выполняет итерацию по собственным свойствам объекта и _.forEach выполняет итерацию по каждому элементу массива.

var identity = function(x) {return x;};
var reverseMapFromMap = function(map, callback) {
    callback = callback || identity; //callback helps to parseInt numeric values, otherwise every object key is a string by default
    var reversedMap = {};
    _.forOwn(map, function(value, key) {
        key = callback(key);
        if (_.isUndefined(reversedMap[value]))
            reversedMap[value] = [key];
        else
            reversedMap[value].push(key);
    });
    return reversedMap;
};

var mapFromReverseMap = function(reverseMap, callback) {
    callback = callback || identity;
    var map = {};
    _.forOwn(reverseMap, function(revMembers, revKey) {
        _.forEach(revMembers, function(revMember) {
            map[revMember] = callback(revKey);
        });
    });
    return map;
};

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

11 голосов | спросил Peeyush Kushwaha 31 PM00000080000000631 2014, 20:29:06

2 ответа


5

Ваш код довольно хорош как есть. Есть и другие способы сделать то же самое, но у вас все в порядке.

Одна вещь, которую я заметил, это то, что вы пропустили фигурные скобки на if...else в reverseFromMap. Мой совет заключается в always использовать скобки, даже для однострочных выражений.

Однако вы также можете сделать

reversedMap[value] || (reversedMap[value] = []);
reversedMap[value].push(key);

Вы можете свернуть это в 1 строку, хотя это немного слишком , если вы спросите меня

(reversedMap[value] || (reversedMap[value] = [])).push(key);
// or 
(reversedMap[value] = reversedMap[value] || []).push(key);

Или просто уменьшите его до трехзначного

reversedMap[value] ? reversedMap[value].push(key) : reversedMap[value] = [key];

Проверка _.isUndefined действительно не требуется, поскольку reversedMap[key] является либо непустым массивом, либо ложным.

Другие мелочи:

  • Я бы предпочел имя iterator вместо callback, но lo-dash использует callback в подобных ситуациях, поэтому придерживаться этого соглашения в порядке.

  • Вам необязательно нужна функция identity, как вы могли бы также сделать

    x = callback ? callback(x) : x;
    

    и просто не беспокоиться о функции идентификации. Это может быть или не быть быстрее - я не сравнивал это. В любом случае оба подхода работают очень хорошо, поэтому нет необходимости что-то менять.

  • Если вам нужна функция идентификации, lo-dash имеет один встроенный (_.identity). Сохраняет необходимость распечатывать его.

  • Наконец, если вы хотите быть строгим, вы можете проверить, является ли callback функцией вместо того, чтобы просто делать «по умолчанию» если ложь ". Например.

    callback = _.isFunction(callback) ? callback : _.identity;
    // or just plain JS
    callback = typeof callback === 'function' ? callback : _.identity;
    

    Лично я бы это пропустил. Если люди передают что-то, что не является функцией, это их ошибка. Кроме того, я бы предпочел, чтобы код вышел из строя громче, чем заставить его молча игнорировать мой (ошибочный) аргумент callback и по умолчанию использовать что-то еще, не сообщая меня. Тем не менее, проверка типов - это вариант, поэтому я подумал, что упомянул об этом.

Что касается альтернатив общему подходу, я бы использовал _.transform (который в основном совпадает с reduce /foldl, но нацелены на объекты)

var reverseMapFromMap = function(map, callback) {
  callback = callback || _.identity;
  return _.transform(map, function (memo, value, key) {
    key = callback(key);
    memo[value] || (memo[value] = []);
    memo[value].push(key);
  }, {});
}

И снова для mapFromReverseMap

var mapFromReverseMap = function(reverseMap, callback) {
  callback = callback || _.identity;
  return _.transform(reverseMap, function (memo, keys, value) {
    _.forEach(keys, function (key) {
      memo[callback(key)] = value;
    });
  }, {});
};
ответил Flambino 31 PM00000090000002331 2014, 21:18:23
4

Я бы рекомендовал использовать встроенные методы, когда сможешь. В этом случае я не похож на LoDash. Вы можете использовать Object.keys и reduce:

var reverseMapFromMap = function(map, f) {
  return Object.keys(map).reduce(function(acc, k) {
    acc[map[k]] = (acc[map[k]] || []).concat((f || id)(k))
    return acc
  },{})
}

var mapFromReverseMap = function(rMap, f) {
  return Object.keys(rMap).reduce(function(acc, k) {
    rMap[k].forEach(function(x){acc[x] = (f || id)(k)})
    return acc
  },{})
}

Я переименовал identity в id , так как это хорошо известная функция в функциональном программировании. Я не думаю, что это создаст путаницу, и позволит вам легко передать ее в один лайнер, как показано выше.

Вышеупомянутое работает с вашим примером http://jsbin.com/cadig/1/edit. Но я бы рекомендовал вам отменить аргументы, поэтому обратный вызов на первом месте, а приемник - последним, поэтому он лучше работает с композицией.

Помимо отсутствующих фигурных скобок в операторе if, я бы сказал, что ваш код выглядит неплохо. Однако довольно часто приходится использовать одну или две переменные букв в функциональном программировании, поскольку эти преобразования применимы ко многим вещам в теории (полиморфизм), и у нас есть только несколько объектов. f явно означает «функция», например, и x - любое значение, где k является «ключом». После привыкания к этим соглашениям строка вроде:

rMap[k].forEach(function(x){acc[x] = (f || id)(k)})

кажется более быстрым для визуального анализа, чем:

rMap[key].forEach(function(value) {
  acc[value] = (callback || identity)(key)
})

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

rMap[k].forEach(x => acc[x] = (f || id)(k))
ответил elclanrs 1 stEurope/Moscowp30Europe/Moscow09bEurope/MoscowMon, 01 Sep 2014 02:38:18 +0400 2014, 02:38:18

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132