2014-02-10 6 views
3

Eloquent JavaScript and High Order Functions을 통해 작업 - 섹션은 Functional Programming입니다.이 reduce 함수를 사용할 수 없습니다.

reduce 함수를 정의하고 더 높은 차수의 함수 countWords();에서 사용하려고합니다.이 배열은 주어진 배열을 취하여 각 특정 값이있는 횟수를 계산하여 객체에 넣습니다.

e.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"--- (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; 
} 

있어야 할

+2

그보다 더 잘못된 것이 있습니다. – JayC

+0

네, 그 점에 유의하십시오. 그래서 jsbin 링크를 추가합니다. 모두 수정하십시오. –

4

jsbin here 당신의 당신이 작동하는지 말에도 불구하고, 실제로 고장 감소 원본.

여기 당신이 게시물의 두 번째 절반 뭘 하려는지 전혀 확실하지 않다, 실제로 말했다

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} 

을 기능하는 reduce입니다. forEachreduce에 대한 구현을 작성하여 작동 원리를 이해하고 있습니까?


나는 연구에 대한 호기심 분들이 밖으로

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 

등이

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

그리고 reduce 같은 forEach를 작성합니다 끌어 내다 콜백, 나는 native .reduce callback

0

난 당신이 forEachreduce가 (브라우저 버그를 무시)를 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를 인수가 "length"인 O의 [Get]] 내부 메소드를 호출 한 결과로 보겠습니다.
  3. len을 ToUint32 (lenValue)로 설정하십시오.
  4. IsCallable (callbackfn)이 false 인 경우 TypeError 예외를 발생시킵니다.
  5. thisArg가 제공된 경우 T를 thisArg로 설정하십시오. 그렇지 않으면 T는 정의되지 않습니다.
  6. K < 렌 동안 K 0
  7. 반복하자
  8. 하자 ToString Pk의 수 (K).
  9. kPresent를 인수 Pk를 사용하여 [[HasProperty]] 내부 메소드 O를 호출 한 결과라고 합니다.
  10. kPresent가 참일 경우
  11. kValue를 인수 Pk로 O의 [Get] 내부 메소드를 호출 한 결과라고 합니다.
  12. 호출 복귀가 정의 1.
  13. 의해 kValue, K 및 O.
  14. 증가 (k)를 포함하는이 인자 값리스트로서 T와 callbackfn의 [[전화] 내부 방법.

foreach는 방법의 길이 속성의 foreach 함수 의도적 일반 1.

NOTE이고; 이 값이 Array 객체 일 필요는 없습니다. 따라서 다른 종류의 객체로 전송하여 메소드로 사용할 수 있습니다. forEach 함수가 호스트 객체에 성공적으로 적용될 수 있는지 여부는 구현에 따라 다릅니다.

-

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

callbackfn 네 개의 인자를받는 함수해야한다.reduce는 콜백을 함수로, 배열에있는 각 요소에 대해 한 번 오름차순으로 호출합니다.

callbackfn은 previousValue (또는 callbackfn에 대한 이전 호출의 값), currentValue (현재 요소의 값), currentIndex 및 통과되는 객체의 네 가지 인수로 호출됩니다. 콜백이 처음 호출되면 previousValue 및 currentValue는 두 값 중 하나가 될 수 있습니다. reduce에 대한 호출에서 initialValue가 제공된 경우 previousValue는 initialValue와 같고 currentValue는 배열의 첫 번째 값과 같습니다. initialValue가 제공되지 않으면 previousValue는 배열의 첫 번째 값과 같고 currentValue는 두 번째 값과 같습니다. 배열에 요소가없고 initialValue가 제공되지 않으면 TypeError입니다.

reduce는 호출 된 객체를 직접 변경하지 않지만 callbackfn을 호출하여 객체를 변경할 수 있습니다.

reduce로 처리되는 요소의 범위는 callbackfn을 처음 호출하기 전에 설정됩니다. reduce를 호출 한 후에 배열에 추가되는 요소는 callbackfn에 의해 방문되지 않습니다. 배열의 기존 요소가 변경되면 callbackfn에 전달 된 값은 reduce를 방문 할 때 값이됩니다. reduce를 호출 한 후 삭제 된 요소는 방문하기 전과 방문하기 전에 방문하지 않습니다. 줄이거 방법은 하나 개 또는 두 개의 인수로 호출

는 다음 단계가 수행됩니다

  1. 하자 O 인수로이 값을 전달 ToObject를 호출의 결과.
  2. lenValue를 인수가 "length"인 O의 [Get]] 내부 메소드를 호출 한 결과로 보겠습니다.
  3. len을 ToUint32 (lenValue)로 설정하십시오.
  4. IsCallable (callbackfn)이 false 인 경우 TypeError 예외를 발생시킵니다.
  5. len이 0이고 initialValue가 없으면 TypeError 예외가 발생합니다.
  6. 로 초기 후,로 초기에 설정
  7. 누산기 존재하면 k는 0
  8. 을하자.
  9. 그렇지 않으면 initialValue가 없습니다.
  10. kPresent를 false로 설정하십시오.
  11. 반복, kPresent가 거짓이고 k < len
  12. Pk를 ToString (k)로 지정하십시오.
  13. kPresent를 인수 Pk를 사용하여 [[HasProperty]] 내부 메소드 O를 호출 한 결과라고 합니다.
  14. kPresent가 참일 경우
  15. 누적 기는 인수 Pk로 O의 내부 메소드 [[Get]]을 호출 한 결과입니다.
  16. k를 1 늘립니다.
  17. kPresent가 false 인 경우 TypeError 예외를 발생시킵니다.
  18. 반복하면서, k < len
  19. Pk를 ToString (k)이라고합시다.
  20. kPresent를 인수 Pk를 사용하여 [[HasProperty]] 내부 메소드 O를 호출 한 결과라고 합니다.
  21. kPresent가 참일 경우
  22. kValue를 인수 Pk로 O의 [Get] 내부 메소드를 호출 한 결과라고 합니다.
  23. 하자 누산기 호출의 결과에서 [[전화]]를이 값과 인수 목록 함유 어큐뮬레이터 kValue, K 및 O와 같은 정의로 callbackfn 내부 방법
  24. 확대 K 1.
  25. 하여 누적 계산기를 반환합니다.

줄이거있어서의 길이 속성은 함수를 감소 1.

NOTE 의도적 총칭이고; 이 값이 Array 객체 일 필요는 없습니다. 따라서 다른 종류의 객체로 전송하여 메소드로 사용할 수 있습니다. reduce 함수를 호스트 객체에 성공적으로 적용 할 수 있는지 여부는 구현에 따라 다릅니다.

내게는 (이것들은 100 % 사양이 아니지만 가까운) 쓰고 내 개인 라이브러리에 보관하십시오. 이것은 당신이 무엇을하고 있는지에 대한 약간의 OTT 수 있습니다 물론 jsFiddle

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); 

. :)