Вопрос по абстрагированию JavaScript

308
21 января 2017, 13:10

Всем привет. Изучаю JavaScript, читаю учебник "Выразительный JavaScript" и дошел до интересной задачи в конце пятой главы.

Задание выглядит так:

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

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

В общем вот код, он работает прекрасно, всё как нужно отрабатывает. Принёс Вам его на суд, что Вы скажете? Есть какие-то явные огрехи и говнокод?

Array.prototype.groupBy = function(acto) {
    var tempObj = {}; //Создаем пустой объект
    for (var i=0; i<this.length; i++){ //Запускаем проход по массиву
        var temp = acto(this[i]); //Создаём переменную, куда помещаем результат вызова функции переданной аргументом. Условимся о том, что функция, передаваемая аргументом, должна возвращать строку
        if (!tempObj[temp]) { //Проверяем наш объект и если в нём нет свойства с таким именем, то 
            tempObj[temp] = [this[i]]; //Создаём свойство с таким именем куда помещаем массив, в который, в свою очередь, помещаем элемент итерации
        } else { //Иначе
            tempObj[temp].push(this[i]); //в массив-свойство объекта добавляем результат операции
        }
    }
    return tempObj; //Возвращаем объект
}

Пример работы:

Array.prototype.groupBy = function(acto) { 
  var tempObj = {}; //Создаем пустой объект 
  for (var i = 0; i < this.length; i++) { //Запускаем проход по массиву 
    var temp = acto(this[i]); //Создаём переменную, куда помещаем результат вызова функции переданной аргументом. Условимся о том, что функция, передаваемая аргументом, должна возвращать строку 
    if (!tempObj[temp]) { //Проверяем наш объект и если в нём нет свойства с таким именем, то  
      tempObj[temp] = [this[i]]; //Создаём свойство с таким именем куда помещаем массив, в который, в свою очередь, помещаем элемент итерации 
    } else { //Иначе 
      tempObj[temp].push(this[i]); //в массив-свойство объекта добавляем результат операции 
    } 
  } 
  return tempObj; //Возвращаем объект 
} 
 
 
var arrayWithTrash = [1, 2, 3, "A", "B", "C", 4, 5, 6, true, [1, 2, 3], { 
  "a": 250 
}]; 
 
console.log( 
  arrayWithTrash.groupBy(function(elemArray) { 
    if (typeof elemArray === "number") return "Number"; 
    else if (typeof elemArray === "string") return "String"; 
    else if (typeof elemArray === "boolean") return "Boolean"; 
    else return "Other"; 
  }) 
);
.as-console-wrapper { 
  min-height: 100% !important; 
  top: 0; 
}

Answer 1

Во-первых, добавлять перечисляемое свойство в массив - ужасная идея! Как минимум, его следует сделать неперечисляемым. Для этого можно использовать Object.defineProperty:

Object.defineProperty(Array.prototype, "groupBy", {
  value: function (acto) {
    // ...
  },
});

Во-вторых, лучше давать переменным понятные названия. Не acto, temp, tempObj - а keySelector, key, result.

В-третьих, у вас будут баги с такими ключами, как toString. Чтобы создаваемый пустой объект был реально пустым - надо использовать не {}, а Object.create(null).

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

Answer 2

Забавная задачка.
Но простая, поэтому код тоже простой, оптимизировать нечего особо (разве что добавить проверок на входные данные).
Изменять/добавлять прототипы встроенных объектов - плохо, не делайте этого.

const groupBy = (a, fn, full = {}) => a.forEach((e, _) => (_ = fn(e) + '') && (full.hasOwnProperty(_) ? full[_].push(e) : full[_] = [e])) || full; 
 
var arrayWithTrash = [1, 2, 3, "A", "B", "C", 4, 5, 6, true, [1, 2, 3], {a: 250}], 
    fn = function(elemArray) { 
  if (typeof elemArray === "number") return "Number"; 
  else if (typeof elemArray === "string") return "String"; 
  else if (typeof elemArray === "boolean") return "Boolean"; 
  else return "Other"; 
}; 
 
console.info(groupBy(arrayWithTrash, fn));
.as-console-wrapper { 
  min-height: 100% !important; 
  top: 0; 
}

READ ALSO
JavaSript вывод цепочки сообщений в console [требует правки]

JavaSript вывод цепочки сообщений в console [требует правки]

Всю служебную информацию записывать в консоль (старт прогресса / старт анимации ячейки / окончание анимации ячейки / окончание прогресса)

284
ES6 import class - babel error

ES6 import class - babel error

При импорте 2 классов в файле bootstrapjs выдает ошибку:

379
Отмена return false у дочерних элементов

Отмена return false у дочерних элементов

Есть блок div, в него вложена ссылка На блоке висит функция, которая возращает return false по клику на этот блок

299
Обработка запроса удаленно по выбору select

Обработка запроса удаленно по выбору select

Друзья, помогите разобраться пожалуйста! Пытаюсь разобраться с jquery, задача следующая: при выборе марки машины из селекта, в соседнем div появляется...

287