2014-02-10 35 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} 

即使這不:

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

「ie。This works」--- it can not and it [does not](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鏈接。將它們全部更正。 –

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} 

這麼說,我不是很確定你想與你的訊息下半年做什麼。你只是想寫forEachreduce的實現,所以你可以理解它們是如何工作的?


我會寫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 

對於那些好奇的關於R得出的回調,我匹配了native .reduce callback

0

我想,如果你想forEachreduce是相似的(簡單)或儘可能接近/合理的ECMA5規範(忽略瀏覽器的bug)依靠,我喜歡儘可能接近/合理。

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成爲用參數「length」調用O的[[Get]]內部方法的結果。
  3. 讓len成爲ToUint32(lenValue)。
  4. 如果IsCallable(callbackfn)爲false,則引發TypeError異常。
  5. 如果提供了這個Arg,讓T爲thisArg;否則讓T不確定。
  6. 令k爲0。
  7. 重複,按住k < len個
  8. 讓PK是的ToString(K)。
  9. 設kPresent是用參數Pk調用O的[[HasProperty]]內部方法的結果。
  10. 如果kPresent爲真,那麼
  11. 設置kValue是用參數Pk調用O的[[Get]]內部方法的結果。
  12. 呼叫callbackfn的有T 1。
  13. 返回未定義的[[調用]]內部方法作爲含有kValue,k和O.
  14. 增加k處的該值和參數列表。

在foreach方法的長度屬性是1

注意在foreach功能是有意通用的;它不要求它的這個值是一個Array對象。因此可以將其轉換爲其他類型的對象以用作方法。 forEach函數是否可以成功應用於主機對象取決於實現。

-

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

callbackfn應該是一個函數,它有四個參數。作爲函數,按照升序的順序減少對該數組中存在的每個元素的回調。

使用四個參數調用callbackfn:previousValue(或前一次調用callbackfn的值),currentValue(當前元素的值),currentIndex和遍歷的對象。第一次調用回調函數時,previousValue和currentValue可以是兩個值中的一個。如果在調用中提供了initialValue以reduce,則previousValue將等於initialValue,而currentValue將等於數組中的第一個值。如果未提供initialValue,則previousValue將等於數組中的第一個值,而currentValue將等於第二個值。如果數組不包含任何元素並且未提供initialValue,則它是一個TypeError。

reduce不會直接改變它被調用的對象,但對象可能會被callbackfn的調用突變。

reduce處理的元素範圍在第一次調用callbackfn之前設置。 callbackfn不會訪問在調用reduce之後追加到數組的元素。如果數組的現有元素髮生更改,則傳遞給callbackfn的值將是減少訪問時的值;在調用之後刪除的元素在訪問開始之前和訪問之前不會被訪問。

當減少方法被稱爲具有一個或兩個參數,採取以下步驟:

  1. 令O是調用ToObject傳遞該值作爲參數的結果。
  2. 讓lenValue成爲用參數「length」調用O的[[Get]]內部方法的結果。
  3. 讓len成爲ToUint32(lenValue)。
  4. 如果IsCallable(callbackfn)爲false,則引發TypeError異常。
  5. 如果len爲0且initialValue不存在,則引發TypeError異常。
  6. 令k爲0。
  7. 如果初值存在,則
  8. 設置累加器爲InitialValue。
  9. 否則,initialValue不存在
  10. 讓kPresent爲假。
  11. 重複,而kPresent爲false並且k < len
  12. 設Pk爲ToString(k)。
  13. 設kPresent是用參數Pk調用O的[[HasProperty]]內部方法的結果。
  14. 如果kPresent爲真,那麼
  15. 讓累加器是用參數Pk調用O的[[Get]]內部方法的結果。
  16. 將k增加1.
  17. 如果kPresent爲false,則引發TypeError異常。
  18. 重複,而k < len
  19. 設Pk爲ToString(k)。
  20. 設kPresent是用參數Pk調用O的[[HasProperty]]內部方法的結果。
  21. 如果kPresent爲真,那麼
  22. 設置kValue是用參數Pk調用O的[[Get]]內部方法的結果。
  23. 設累加器是調用的結果[[調用]具有未定義作爲此值和參數列表包含累加器,kValue,k和O. callbackfn的內部方法
  24. 增加K內由1
  25. 返回累加器。

的減少方法的長度屬性是1

注意的降低功能被有意通用的;它不要求它的這個值是一個Array對象。因此可以將其轉換爲其他類型的對象以用作方法。 reduce函數是否可以成功應用於主機對象取決於實現。

對我來說,我會寫(這些不是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。 :)