简体   繁体   English

无法使此缩减功能起作用

[英]Can't get this reduce function to work

Working through Eloquent JavaScript and High Order Functions - section in Functional Programming . 使用Eloquent JavaScript and High Order Functions - Functional Programming部分。

Trying to define a reduce function and use it in a higher order function countWords(); 尝试定义reduce函数并在更高阶函数countWords();使用它countWords(); , which takes a given array and counts number of times each particular value is present and puts it in an object. ,它接受一个给定的数组并计算每个特定值存在的次数并将其放入一个对象中。

Ie this works: 即这是有效的:

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}

Ie this does not: 即这不是:

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.

Any help on this would be great. 对此的任何帮助都会很棒。 Thanks. 谢谢。

Your original reduce is actually broken, despite you saying that it works. 尽管您说它有效,但您的原始缩减实际上已被破坏。

Here's a reduce that actually functions 这是一个实际起作用的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}

That said, I'm not entirely sure what you're trying to do with the second half of your post. 也就是说,我不完全确定你在帖子的后半部分要做什么。 Are you just trying to write implementations for forEach and reduce so you can understand how they work? 您是否只是尝试为forEach编写实现并reduce以便您可以了解它们的工作原理?


I would write the forEach like this 我会写这样的forEach

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

And reduce like this 并且像这样reduce

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

Test them out 测试它们

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

For those curious about reduce callback, I matched the native .reduce callback 对于那些对减少回调感兴趣的人 ,我匹配了原生的.reduce回调

I guess it depends on if you want forEach and reduce to be similar (simple) or as close as possible/reasonable to the ECMA5 spec (ignoring browser bugs), I like close as possible/reasonable. 我想这取决于你是否希望forEachreduce与ECMA5规范相似(简单)或尽可能接近/合理(忽略浏览器错误),我尽可能接近/合理。

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

callbackfn should be a function that accepts three arguments. callbackfn应该是一个接受三个参数的函数。 forEach calls callbackfn once for each element present in the array, in ascending order. forEach按升序为数组中的每个元素调用一次callbackfn。 callbackfn is called only for elements of the array which actually exist; callbackfn仅针对实际存在的数组元素调用; it is not called for missing elements of the array. 它不会被调用缺少数组的元素。

If a thisArg parameter is provided, it will be used as the this value for each invocation of callbackfn. 如果提供了thisArg参数,则每次调用callbackfn时它都将用作此值。 If it is not provided, undefined is used instead. 如果未提供,则使用undefined。

callbackfn is called with three arguments: the value of the element, the index of the element, and the object being traversed. 使用三个参数调用callbackfn:元素的值,元素的索引和被遍历的对象。

forEach does not directly mutate the object on which it is called but the object may be mutated by the calls to callbackfn. forEach不直接改变调用它的对象,但是对象可能会被callbackfn调用变异。

The range of elements processed by forEach is set before the first call to callbackfn. forEach处理的元素范围在第一次调用callbackfn之前设置。 Elements which are appended to the array after the call to forEach begins will not be visited by callbackfn. callbackfn将不会访问在调用forEach之后附加到数组的元素。 If existing elements of the array are changed, their value as passed to callback will be the value at the time forEach visits them; 如果更改了数组的现有元素,则传递给回调的值将是每次访问它们时的值; elements that are deleted after the call to forEach begins and before being visited are not visited. 不会访问在调用forEach开始之后和访问之前删除的元素。

When the forEach method is called with one or two arguments, the following steps are taken: 使用一个或两个参数调用forEach方法时,将执行以下步骤:

  1. Let O be the result of calling ToObject passing the this value as the argument. 设O是调用ToObject传递此值作为参数的结果。
  2. Let lenValue be the result of calling the [[Get]] internal method of O with the argument "length". 设lenValue是使用参数“length”调用O的[[Get]]内部方法的结果。
  3. Let len be ToUint32(lenValue). 设len为ToUint32(lenValue)。
  4. If IsCallable(callbackfn) is false, throw a TypeError exception. 如果IsCallable(callbackfn)为false,则抛出TypeError异常。
  5. If thisArg was supplied, let T be thisArg; 如果提供thisArg,则让T为thisArg; else let T be undefined. 否则让T不确定。
  6. Let k be 0. 设k为0。
  7. Repeat, while k < len 重复,而k <len
  8. Let Pk be ToString(k). 设Pk为ToString(k)。
  9. Let kPresent be the result of calling the [[HasProperty]] internal method of O with argument Pk. 设kPresent是用参数Pk调用O的[[HasProperty]]内部方法的结果。
  10. If kPresent is true, then 如果kPresent为真,那么
  11. Let kValue be the result of calling the [[Get]] internal method of O with argument Pk. 设kValue是用参数Pk调用O的[[Get]]内部方法的结果。
  12. Call the [[Call]] internal method of callbackfn with T as the this value and argument list containing kValue, k, and O. 使用T作为此值和参数列表调用callbackfn的[[Call]]内部方法,其中包含kValue,k和O.
  13. Increase k by 1. 将k增加1。
  14. Return undefined. 返回undefined。

The length property of the forEach method is 1. forEach方法的length属性为1。

NOTE The forEach function is intentionally generic; 注意forEach函数是有意通用的; it does not require that its this value be an Array object. 它不要求它的这个值是一个Array对象。 Therefore it can be transferred to other kinds of objects for use as a method. 因此,它可以转移到其他类型的对象以用作方法。 Whether the forEach function can be applied successfully to a host object is implementation-dependent. forEach函数是否可以成功应用于宿主对象是依赖于实现的。

- -

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

callbackfn should be a function that takes four arguments. callbackfn应该是一个带有四个参数的函数。 reduce calls the callback, as a function, once for each element present in the array, in ascending order. reduce作为函数调用回调,对于数组中存在的每个元素,按升序调用一次。

callbackfn is called with four arguments: the previousValue (or value from the previous call to callbackfn), the currentValue (value of the current element), the currentIndex, and the object being traversed. callbackfn使用四个参数调用:previousValue(或前一次调用callbackfn的值),currentValue(当前元素的值),currentIndex和被遍历的对象。 The first time that callback is called, the previousValue and currentValue can be one of two values. 第一次调用回调时,previousValue和currentValue可以是两个值之一。 If an initialValue was provided in the call to reduce, then previousValue will be equal to initialValue and currentValue will be equal to the first value in the array. 如果在reduce的调用中提供了initialValue,则previousValue将等于initialValue,currentValue将等于数组中的第一个值。 If no initialValue was provided, then previousValue will be equal to the first value in the array and currentValue will be equal to the second. 如果没有提供initialValue,那么previousValue将等于数组中的第一个值,currentValue将等于第二个值。 It is a TypeError if the array contains no elements and initialValue is not provided. 如果数组不包含元素且未提供initialValue,则为TypeError。

reduce does not directly mutate the object on which it is called but the object may be mutated by the calls to callbackfn. reduce不会直接改变调用它的对象,但是对象可能会被callbackfn的调用所突变。

The range of elements processed by reduce is set before the first call to callbackfn. reduce处理的元素范围在第一次调用callbackfn之前设置。 Elements that are appended to the array after the call to reduce begins will not be visited by callbackfn. callbackfn将不会访问在调用reduce开始后附加到数组的元素。 If existing elements of the array are changed, their value as passed to callbackfn will be the value at the time reduce visits them; 如果更改了数组的现有元素,则传递给callbackfn的值将是reduce访问它们时的值; elements that are deleted after the call to reduce begins and before being visited are not visited. 不会访问在调用reduce开始之后和访问之前删除的元素。

When the reduce method is called with one or two arguments, the following steps are taken: 使用一个或两个参数调用reduce方法时,将执行以下步骤:

  1. Let O be the result of calling ToObject passing the this value as the argument. 设O是调用ToObject传递此值作为参数的结果。
  2. Let lenValue be the result of calling the [[Get]] internal method of O with the argument "length". 设lenValue是使用参数“length”调用O的[[Get]]内部方法的结果。
  3. Let len be ToUint32(lenValue). 设len为ToUint32(lenValue)。
  4. If IsCallable(callbackfn) is false, throw a TypeError exception. 如果IsCallable(callbackfn)为false,则抛出TypeError异常。
  5. If len is 0 and initialValue is not present, throw a TypeError exception. 如果len为0且initialValue不存在,则抛出TypeError异常。
  6. Let k be 0. 设k为0。
  7. If initialValue is present, then 如果存在initialValue,那么
  8. Set accumulator to initialValue. 将累加器设置为initialValue。
  9. Else, initialValue is not present 否则,initialValue不存在
  10. Let kPresent be false. 让kPresent为假。
  11. Repeat, while kPresent is false and k < len 重复,而kPresent为false且k <len
  12. Let Pk be ToString(k). 设Pk为ToString(k)。
  13. Let kPresent be the result of calling the [[HasProperty]] internal method of O with argument Pk. 设kPresent是用参数Pk调用O的[[HasProperty]]内部方法的结果。
  14. If kPresent is true, then 如果kPresent为真,那么
  15. Let accumulator be the result of calling the [[Get]] internal method of O with argument Pk. 让累加器是用参数Pk调用O的[[Get]]内部方法的结果。
  16. Increase k by 1. 将k增加1。
  17. If kPresent is false, throw a TypeError exception. 如果kPresent为false,则抛出TypeError异常。
  18. Repeat, while k < len 重复,而k <len
  19. Let Pk be ToString(k). 设Pk为ToString(k)。
  20. Let kPresent be the result of calling the [[HasProperty]] internal method of O with argument Pk. 设kPresent是用参数Pk调用O的[[HasProperty]]内部方法的结果。
  21. If kPresent is true, then 如果kPresent为真,那么
  22. Let kValue be the result of calling the [[Get]] internal method of O with argument Pk. 设kValue是用参数Pk调用O的[[Get]]内部方法的结果。
  23. Let accumulator be the result of calling the [[Call]] internal method of callbackfn with undefined as the this value and argument list containing accumulator, kValue, k, and O. 令累加器是使用undefined调用callbackfn的[[Call]]内部方法的结果,因为该值和参数列表包含累加器,kValue,k和O.
  24. Increase k by 1. 将k增加1。
  25. Return accumulator. 返回累加器。

The length property of the reduce method is 1. reduce方法的length属性为1。

NOTE The reduce function is intentionally generic; 注意reduce功能是有意通用的; it does not require that its this value be an Array object. 它不要求它的这个值是一个Array对象。 Therefore it can be transferred to other kinds of objects for use as a method. 因此,它可以转移到其他类型的对象以用作方法。 Whether the reduce function can be applied successfully to a host object is implementation-dependent. reduce函数是否可以成功应用于宿主对象是依赖于实现的。

Which for me I would write (and these are not 100% to spec, but close) and keep in my personal library. 对我来说,我会写(这些不是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);

On jsFiddle jsFiddle上

Of course this may be a little OTT for what you are doing. 当然,对于你正在做的事情,这可能有点OTT。 :) :)

The reason is that your ForEach implementation is wrong. 原因是你的ForEach实现是错误的。 you should set i = 0 ; 你应该设置i = 0 ;

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

There seems to be something wrong. 似乎有些不对劲。 You update an object. 您更新对象。 ++countMap

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

It should be 它应该是

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

I add a jsbin here 在这里添加一个jsbin

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM