如何计算数组值之间的差异,然后对它们取平均值?

时间:2016-10-25 09:24:52

标签: javascript arrays functional-programming

我正试图以功能的方式做到这一点,所以尽管有一些方法我已经可以想到,但我怀疑有更优雅的map / reduce风格技术。

给出这样的数组:

[0, 47.22, 72.65, 126.93, 155.02, 307.46, 410.02, 417.21, 
 507.86, 510.466, 580.88, 661.29, 685.14, 695.86, 780.94,
 913.2, 1352.33, 1382.99, 1435.73, 1462.03, 1495.38, 1518.03,
 1523.58, 1544.3, 1591.21, 1612.03, 1665.99, 1672.62, 11.02, 22.791]

我想计算每个项目及其后续值之间的差异,例如:

[47.22, 25.43, 54.28] // etc

(这些值是聊天消息的时间戳 - 我想有效地查看消息之间的差距,然后计算该用户的平均值。)

我在看reduce()但是很难看到如何让它做我需要的事情。有没有一种优雅的方法来做到这一点,或者我最好的选择是for循环,它会向前看下一个数组项并在那里进行计算?

5 个答案:

答案 0 :(得分:4)

使用reduce的另一种解决方案。当平均值不可计算时,此函数返回合理的NaN结果。

此数据计算出0.7858965517241373的错误答案,因为输入数据是无序的,这是没有意义的。如果您对数据进行排序,则会得到更明智的31.48965517241379秒平均增量。

const averageDelta = ([x,...xs]) => {
  if (x === undefined)
    return NaN
  else
    return xs.reduce(
      ([acc, last], x) => [acc + (x - last), x],
      [0, x]
    ) [0] / xs.length
};

let timestamps = [
  0, 47.22, 72.65, 126.93, 155.02, 307.46, 410.02, 417.21, 
  507.86, 510.466, 580.88, 661.29, 685.14, 695.86, 780.94,
  913.2, 1352.33, 1382.99, 1435.73, 1462.03, 1495.38, 1518.03,
  1523.58, 1544.3, 1591.21, 1612.03, 1665.99, 1672.62, 11.02, 22.791
]

console.log(averageDelta(timestamps))        // 0.7858965517241373
console.log(averageDelta(timestamps.sort())) // 31.48965517241379

但是您使用functional-programming标记了此问题,因此您可能正在寻找更好的解决方案。上面的答案很好,但它混合了几个基本算术计算的问题。 averageDelta在一个函数中计算差异 sum 均值 - 这真是一团糟!

下面我们将分别为每个事项实现一个函数,这样我们就不必那么认真。您还会注意到我已经注意确保每个函数都是total function,该函数适用于其域的所有输入。这意味着averagedelta将为空数组,单数组数组以及具有多个数字的数组返回合理的结果。

const add = (x,y) =>
  x + y
  
const sum = xs =>
  xs.reduce(add, 0)

const average = xs =>
  xs[0] === undefined ? NaN : sum(xs) / xs.length

const delta = ([x,...xs]) =>
  xs.reduce(([acc, last], x) => [[...acc, x-last], x], [[], x]) [0]

let timestamps = [
  0, 47.22, 72.65, 126.93, 155.02, 307.46, 410.02, 417.21, 
  507.86, 510.466, 580.88, 661.29, 685.14, 695.86, 780.94,
  913.2, 1352.33, 1382.99, 1435.73, 1462.03, 1495.38, 1518.03,
  1523.58, 1544.3, 1591.21, 1612.03, 1665.99, 1672.62, 11.02, 22.791
]

console.log(average(delta([])))                // NaN
console.log(average(delta([1])))               // NaN
console.log(average(delta(timestamps)))        // 0.7858965517241373
console.log(average(delta(timestamps.sort()))) // 31.48965517241379

  

警告:timestamps.sort()使用Array.prototype.sort来改变原始数组。考虑到在原始帖子中没有讨论排序,我会将其作为一个问题让你解决,如果它甚至是一个。

答案 1 :(得分:1)

您可以使用reduce()这样做。



var data = [0, 47.22, 72.65, 126.93, 155.02, 307.46, 410.02, 417.21, 
 507.86, 510.466, 580.88, 661.29, 685.14, 695.86, 780.94,
 913.2, 1352.33, 1382.99, 1435.73, 1462.03, 1495.38, 1518.03,
 1523.58, 1544.3, 1591.21, 1612.03, 1665.99, 1672.62, 11.02, 22.791];
 
 var result = data.reduce(function(r, e, i) {
  if(data[i+1]) r.push(Number((data[i+1] - e).toFixed(2)));
  return r;
 }, [])
 
 console.log(result)




答案 2 :(得分:1)

您可以使用Array#reduce并仅使用前一个值。



var array = [0, 47.22, 72.65, 126.93, 155.02, 307.46, 410.02, 417.21, 507.86, 510.466, 580.88, 661.29, 685.14, 695.86, 780.94, 913.2, 1352.33, 1382.99, 1435.73, 1462.03, 1495.38, 1518.03, 1523.58, 1544.3, 1591.21, 1612.03, 1665.99, 1672.62, 11.02, 22.791],
    delta = array.reduce(function (r, a, i, aa) {
        i && r.push(a - aa[i - 1]);
        return r;
    }, []),
    average = delta.reduce(function (a, b) { return a + b; }) / delta.length;

console.log(delta);
console.log(average);

.as-console-wrapper { max-height: 100% !important; top: 0; }




答案 3 :(得分:1)

您可以使用map并返回当前和下一个项目之间的差异

var arr = [0, 47.22, 72.65, 126.93, 155.02, 307.46, 410.02, 417.21, 
 507.86, 510.466, 580.88, 661.29, 685.14, 695.86, 780.94,
 913.2, 1352.33, 1382.99, 1435.73, 1462.03, 1495.38, 1518.03,
 1523.58, 1544.3, 1591.21, 1612.03, 1665.99, 1672.62, 11.02, 22.791];
var output = arr.map( function( item, index, arr ){ 
  return (arr[index+1] - arr[index]); 
});
output.splice(-1,1); //remove last item, it is NaN since it is a difference between undefined and 22.791
console.log("Diff is", output);
var average = output.reduce( function(curr,prev){ return curr+prev; })/output.length;

console.log("average", average);

答案 4 :(得分:1)

使用forEach循环将相邻元素之间的差异添加到新数组中:

var arr = [22, 47.22, 72.65, 126.93, 155.02, 307.46, 410.02, 417.21, 507.86, 510.466, 580.88, 661.29, 685.14, 695.86, 780.94,913.2, 1352.33, 1382.99, 1435.73, 1462.03, 1495.38, 1518.03,1523.58, 1544.3, 1591.21, 1612.03, 1665.99, 1672.62, 11.02, 22.791];

var result = {array:[], sum: 0, current: 0};

arr.forEach(function(element, index, array) {
    this.current = ((array[index + 1] - element) || 0);
    this.sum += this.current;
    this.array.push(this.current.toFixed(3));
}, result);

console.log(result.array);
console.log("Average: ", (result.sum / result.array.length).toFixed(3));
.as-console-wrapper{top:0;max-height:100%!important;}

修改

感谢@naomik提出建议 - 更好地使用reduce 将所有突变保留在回调本地

var arr = [0, 47.22, 72.65, 126.93, 155.02, 307.46, 410.02, 417.21, 507.86, 510.466, 580.88, 661.29, 685.14, 695.86, 780.94,913.2, 1352.33, 1382.99, 1435.73, 1462.03, 1495.38, 1518.03,1523.58, 1544.3, 1591.21, 1612.03, 1665.99, 1672.62, 11.02, 22.791];

var result = arr.reduce(function(acc, element, index, array) {
    acc.sum += element - acc.prev;
    index && acc.array.push((element - acc.prev).toFixed(3));
    acc.prev = element;
    return acc;
}, {array:[], sum: 0, prev: arr[0]});

console.log(result.array);
console.log("Average: ", (result.sum / result.array.length).toFixed(3));
.as-console-wrapper{top:0;max-height:100%!important;}