2014-02-10 4 views
3

Работа через Eloquent JavaScript and High Order Functions - раздел в Functional Programming.Невозможно получить эту функцию уменьшения для работы

Пытается определить функцию reduce и использовать ее в функции более высокого порядка countWords();, которая принимает заданный массив и подсчитывает количество раз, когда каждое конкретное значение присутствует и помещает его в объект.

I.e. это работает:

function combine(countMap, word) { 
    countMap[word] = ++countMap[word] || 1; // made the edit 

    return countMap; 
} 

function countWords(wordArray) { 
    return wordArray.reduce(combine, {}); 
} 

var inputWords = ['Apple', 'Banana', 'Apple', 'Pear', 'Pear', 'Pear']; 

countWords(inputWords); // {Apple: 2, Banana: 1, Pear: 3} 

I.e. это не означает:

function combine(countMap, word) { 
    countMap[word] = ++countMap[word] || 1; 

    return countMap; 
} 

function forEach(array, action) { 
    for (var i = 0; i < array.length; i++) { 
     action(array[i]); 
    } 
} 

function reduce(fn, base, array) { 
    forEach(array, function (element) { 
     base = fn(base, element); 
    }); 

    return base; 
} 

function countWords(wordArray) { 
    return reduce(combine, {}, wordArray); 
} 

var inputWords = ['Apple', 'Banana', 'Apple', 'Pear', 'Pear', 'Pear']; 

countWords(inputWords); // returned this - [object Object] { ... } - this is no longer an issue after fix keeping it noted for reference to the original issue. 

Любая помощь по этому вопросу будет замечательной. Благодарю.

+1

Это может быть лучший вопрос для http://codereview.stackexchange.com. –

+2

«То есть это работает» --- он не может и не [] [http://jsfiddle.net/7Zwc2/) – zerkms

+0

cc @AndersonGreen. –

ответ

-2

Причина в том, что ваша реализация ForEach неверна. вы должны установить i = 0;

function forEach(array, action) { 
    for(var i = 0; i < array.length; i++) { 
     action(array[i]); 
    } 
} 

Кажется, что-то не так. Вы обновляете объект. ++countMap

function combine(countMap, word) { 
    countMap[word] = ++countMap || 1; 
    return countMap; 
} 

Это должно быть

function combine(countMap, word) { 
    countMap[word] = ++countMap[word] || 1; 
    return countMap; 
} 

Я добавить jsbin here

+2

Там что-то не так. – JayC

+0

да, я его заметлю. поэтому я добавляю jsbin link.Correct их всех. –

4

Ваш оригинальный сократить на самом деле сломано, несмотря на вы говорите, что это работает.

Вот reduce, что на самом деле функции

var words = ["foo", "bar", "hello", "world", "foo", "bar"]; 

var wordIndexer = function(map, word) { 
    map[word] = map[word] || 0; 
    map[word]++; 
    return map; 
}; 

var count = word.reduce(wordIndexer, {}); 

console.log(count); 

// Object {foo: 2, bar: 2, hello: 1, world: 1} 

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


Я написал бы forEach как этот

var forEach = function(arr, callback) { 
    for (var i=0, len=arr.length; i<len; i++) { 
    callback(arr[i], i, arr); 
    } 
    return arr; 
}; 

reduce И как этот

var reduce = function(arr, callback, initialValue) { 
    var result = initialValue; 
    forEach(arr, function(elem, idx) { 
    result = callback(result, elem, idx, arr); 
    }); 
    return result; 
}; 

испытания их

var numbers = [10, 20, 30]; 

forEach(numbers, function(num, idx) { 
    console.log(idx, num); 
}); 

// 0, 10 
// 1, 20 
// 2, 30 
//=> [10, 20, 30] 

var n = reduce(numbers, function(sum, num, idx, arr) { 
    return sum = sum + num; 
}, 0); 

console.log(n); 
//=> 60 

Для тех, кто интересуется г Educe обратного вызова, я соответствовал native .reduce callback

0

Я предполагаю, что это зависит от того, если вы хотите forEach и reduce быть похожими (простой) или как можно ближе/разумно к ECMA5 спецификации (игнорируя ошибки браузера), мне нравится как можно ближе/разумным.

Array.prototype.forEach (callbackfn [ , thisArg ])

callbackfn должна быть функция, которая принимает три аргумента. forEach вызывает callbackfn один раз для каждого элемента, присутствующего в массиве, в порядке возрастания.callbackfn вызывается только для элементов существующего массива; он не вызывается для отсутствующих элементов массива.

Если указан параметр thisArg, он будет использоваться как это значение для каждого вызова callbackfn. Если он не указан, вместо него используется undefined.

callbackfn вызывается с тремя аргументами: значением элемента, индексом элемента и пройденным объектом.

forEach не напрямую мутирует объект, на который он вызывается, но объект может быть мутирован вызовами callbackfn.

Диапазон элементов, обработанных forEach, устанавливается перед первым вызовом callbackfn. Элементы, которые добавляются в массив после вызова метода forEach, не будут посещаться callbackfn. Если существующие элементы массива изменены, их значение, переданное обратному вызову, будет значением в то время, когда каждый посетит их; элементы, которые удаляются после вызова метода forEach и до его посещения, не посещаются.

Когда метод Foreach вызывается с одним или двумя аргументами выполняются следующие шаги:

  1. Пусть O будет результатом вызова ToObject передавая это значение в качестве аргумента.
  2. Пусть lenValue является результатом вызова внутреннего метода O [[Get]] с аргументом «length».
  3. Позвольте len быть ToUint32 (lenValue).
  4. Если IsCallable (callbackfn) является ложным, введите исключение TypeError.
  5. Если этотArg был поставлен, пусть T - этоArg; иначе пусть T не определено.
  6. Пусть к 0.
  7. Repeat, в то время как к < Len
  8. Пусть Рк ToString (к).
  9. Пусть kPresent является результатом вызова внутреннего метода O [[HasProperty]] с аргументом Pk.
  10. Если kPresent является истинным, то
  11. Пусть kValue будет результатом вызова внутреннего метода O [[Get]] с аргументом Pk.
  12. Вызвать метод [[Call]] внутренний метод callbackfn с Т в качестве списка этого значения аргумента и содержащего-значение, К и О.
  13. Увеличения к на 1.
  14. Возврат не определен.

Длина свойство метода Foreach равно 1.

Примечание Функция Foreach намеренно родовое; он не требует, чтобы это значение было объектом Array. Поэтому он может быть передан другим типам объектов для использования в качестве метода. Может ли функция forEach успешно применяться к объекту хоста, зависит от реализации.

-

Array.prototype.reduce (callbackfn [ , initialValue ])

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

callbackfn вызывается с четырьмя аргументами: previousValue (или значением предыдущего вызова callbackfn), currentValue (значение текущего элемента), currentIndex и проходящий объект. При первом вызове обратного вызова значение previousValue и currentValue может быть одним из двух значений. Если в вызове было предложено initialValue, то значение предыдущегоValue будет равно initialValue, а currentValue будет равно первому значению в массиве. Если initialValue не было предоставлено, то предыдущее значение Value будет равно первому значению в массиве, а currentValue будет равно второму. Это TypeError, если массив не содержит элементов, а initialValue не предоставляется.

сокращение не напрямую мутирует объект, на который он вызывается, но объект может быть мутирован вызовами callbackfn.

Диапазон элементов, обработанных сокращением, устанавливается перед первым вызовом callbackfn. Элементы, которые добавляются к массиву после начала вызова для отказа, не будут посещаться callbackfn. Если существующие элементы массива изменены, их значение, переданное callbackfn, будет значением, которое в то время уменьшает количество посещений; элементы, которые удаляются после начала вызова до начала и перед посещением, не посещаются.

Когда метод снижения вызывается с одним или двумя аргументами выполняются следующие шаги:

  1. Пусть O будет результатом вызова ToObject передавая это значение в качестве аргумента.
  2. Пусть lenValue является результатом вызова внутреннего метода O [[Get]] с аргументом «length».
  3. Позвольте len быть ToUint32 (lenValue).
  4. Если IsCallable (callbackfn) является ложным, введите исключение TypeError.
  5. Если len равно 0, а initialValue нет, введите исключение TypeError.
  6. Пусть к 0.
  7. Если InitialValue присутствует, то
  8. Установите аккумулятор в InitialValue.
  9. Else, initialValue нет
  10. Пусть kPresent будет ложным.
  11. Повторите, в то время как kPresent является ложным и k < len
  12. Пусть Pk be ToString (k).
  13. Пусть kPresent является результатом вызова внутреннего метода O [[HasProperty]] с аргументом Pk.
  14. Если kPresent является истинным, то
  15. Позвольте аккумулятору быть результатом вызова внутреннего метода O [[Get]] с аргументом Pk.
  16. Увеличение k на 1.
  17. Если kPresent является ложным, введите исключение TypeError.
  18. Повторите, в то время как k < len
  19. Позвольте Pk быть ToString (k).
  20. Пусть kPresent является результатом вызова внутреннего метода O [[HasProperty]] с аргументом Pk.
  21. Если kPresent является истинным, то
  22. Пусть kValue будет результатом вызова внутреннего метода O [[Get]] с аргументом Pk.
  23. Пусть аккумулятор будет результатом вызова метода [[Call]] внутренний метод callbackfn с неопределенным, как это значение и список аргументов, содержащий аккумулятор, к,-значение, и О.
  24. Увеличение к на 1.
  25. Возвратный аккумулятор.

Длина свойство методы уменьшения равно 1.

ПРИМЕЧАНИЕ уменьшает функцию намеренно родовой; он не требует, чтобы это значение было объектом Array. Поэтому он может быть передан другим типам объектов для использования в качестве метода. Может ли функция сокращения успешно применяться к объекту хоста, зависит от реализации.

Который для меня я бы написал (и это не 100% спецификации, но близко) и сохранить в моей личной библиотеке.

function firstToCapital(inputString) { 
    return inputString.charAt(0).toUpperCase() + inputString.slice(1).toLowerCase(); 
} 

function isClass(inputArg, className) { 
    return Object.prototype.toString.call(inputArg) === '[object ' + firstToCapital(className) + ']'; 
} 

function checkObjectCoercible(inputArg) { 
    if (typeof inputArg === 'undefined' || inputArg === null) { 
     throw new TypeError('Cannot convert argument to object'); 
    } 

    return inputArg; 
}; 

function ToObject(inputArg) { 
    checkObjectCoercible(inputArg); 
    if (isClass(inputArg, 'boolean')) { 
     inputArg = new Boolean(inputArg); 
    } else if (isClass(inputArg, 'number')) { 
     inputArg = new Number(inputArg); 
    } else if (isClass(inputArg, 'string')) { 
     inputArg = new String(inputArg); 
    } 

    return inputArg; 
} 

function ToUint32(inputArg) { 
    return inputArg >>> 0; 
} 

function throwIfNotAFunction(inputArg) { 
    if (!isClass(inputArg, 'function')) { 
     throw TypeError('Argument is not a function'); 
    } 

    return inputArg; 
} 

function forEach(array, fn, thisArg) { 
    var object = ToObject(array), 
     length, 
     index; 

    throwIfNotAFunction(fn); 
    length = ToUint32(object.length); 
    for (index = 0; index < length; index += 1) { 
     if (index in object) { 
      fn.call(thisArg, object[index], index, object); 
     } 
    } 
} 

function reduce(array, fn, initialValue) { 
    var object = ToObject(array), 
     accumulator, 
     length, 
     kPresent, 
     index; 

    throwIfNotAFunction(fn); 
    length = ToUint32(object.length); 
    if (!length && arguments.length === 2) { 
     throw new TypeError('reduce of empty array with no initial value'); 
    } 

    index = 0; 
    if (arguments.length > 2) { 
     accumulator = initialValue; 
    } else { 
     kPresent = false; 
     while (!kPresent && index < length) { 
      kPresent = index in object; 
      if (kPresent) { 
       accumulator = object[index]; 
       index += 1; 
      } 
     } 

     if (!kPresent) { 
      throw new TypeError('reduce of empty array with no initial value'); 
     } 
    } 

    while (index < length) { 
     if (index in object) { 
      accumulator = fn.call(undefined, accumulator, object[index], index, object); 
     } 

     index += 1; 
    } 

    return accumulator; 
} 

function keys(object) { 
    if (!isClass(object, 'object') && !isClass(object, 'function')) { 
     throw new TypeError('Argument must be an object or function'); 
    } 

    var props = [], 
     prop; 

    for (prop in object) { 
     if (object.hasOwnProperty(prop)) { 
      props.push(prop); 
     } 
    } 

    return props; 
} 

var inputWords = ['Apple', 'Banana', 'Apple', 'Pear', 'Pear', 'Pear']; 

var counts = reduce(inputWords, function (previous, element) { 
    previous[element] = ++previous[element] || 1; 

    return previous; 
}, {}); 

forEach(keys(counts), function (key) { 
    console.log(key, this[key]); 
}, counts); 

На jsFiddle

Конечно, это может быть немного OTT за то, что вы делаете. :)

 Смежные вопросы

  • Нет связанных вопросов^_^