Ramda JS最大元素

时间:2018-10-05 14:48:32

标签: ramda.js

我想知道从数组中获取最大元素的最佳方法是什么。

例如,我的区域具有温度:

let regions = [{name: 'alabama', temp: 20}, {name: 'newyork', temp: 30}...];

可以一行完成,但我想表现出色。

我只想遍历数组一次。

如果一个以上的区域具有相同的最高温度,我想全部获取

与带有临时变量的过程代码相比,您知道一种使代码更紧凑的方法吗?

如果可以通过“函数式编程”方式完成,那就太好了。

这是示例过程代码:

regions = [{name:'asd', temp: 13},{name: 'fdg', temp: 30}, {name: 'asdsd', temp: 30}]
maxes = []
max = 0
for (let reg of regions) {
    if (reg.temp > max) {
        maxes = [reg];
        max = reg.temp
    } else if (reg.temp == max) {
        maxes.push(reg)
    } else {
        maxes =[]
    }
}

3 个答案:

答案 0 :(得分:2)

使用R.pipe

  1. 按照temp的值
  2. 对对象进行分组
  3. 将组的对象转换为成对的数组
  4. 使用最大密钥(temp)将配对减少为一对。
  5. 从对中返回值

const { pipe, groupBy, prop, toPairs, reduce, maxBy, head, last } = R;

const regions = [
  {name: 'california', temp: 30},
  {name: 'alabama', temp: 20},
  {name: 'newyork', temp: 30}
];

const result = pipe(
  groupBy(prop('temp')),
  toPairs,
  reduce(maxBy(pipe(head, Number)), [-Infinity]),
  last
)(regions);

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>

答案 1 :(得分:2)

另一种Ramda方法:

const {reduce, append} = R

const regions = [{name:'asd', temp: 13},{name: 'fdg', temp: 30}, {name: 'asdsd', temp: 30}]

const maxTemps = reduce(
  (tops, curr) => curr.temp > tops[0].temp ? [curr] : curr.temp === tops[0].temp ? append(curr, tops) : tops,
  [{temp: -Infinity}]
)

console.log(maxTemps(regions))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>

此版本仅迭代列表一次。但这有点难看。

除非测试表明性能是我的应用程序中的问题,否则我通常更喜欢Ori Drori的版本。即使使用我的评论中的修复程序,我也认为代码比此代码更易于理解。 (如果只有两种情况,那就不是真的。(例如<>=。)但是,当存在三种情况时,这很难看懂,但是我们可以对其进行格式化。

但是如果性能确实是一个主要问题,那么您的原始代码也可能比此代码要快。

答案 2 :(得分:2)

一种不同的处理方法(虽然稍微冗长一些)是创建一些帮助程序来一般地负责折叠事物列表以提取最大值列表。

我们可以通过定义一个Semigroup包装类(也可以是一个普通函数而不是一个类)来做到这一点。

const MaxManyBy = fn => class MaxMany {
  constructor(values) {
    this.values = values
  }
  concat(other) {
    const otherValue = fn(other.values[0]),
          thisValue  = fn(this.values[0])

    return otherValue > thisValue ? other
         : otherValue < thisValue ? this
         : new MaxMany(this.values.concat(other.values))
  }
  static of(x) {
    return new MaxMany([x])
  }
}

此类的主要目的是能够通过比较其中包含的值来合并两个列表,并且每个列表都包含相同的可比较值。

我们现在可以引入一个新的辅助函数,该函数将一些函数应用于列表的每个值,然后使用concat将它们组合在一起。

const foldMap = (fn, [x, ...xs]) =>
  xs.reduce((acc, next) => acc.concat(fn(next)), fn(x))

有了这些帮助器,我们现在可以创建一个函数来从示例中获取最高温度。

const maxTemps = xs =>
  foldMap(MaxManyBy(({temp}) => temp).of, xs).values

maxTemps([
  {name: 'california', temp: 30},
  {name: 'alabama', temp: 20},
  {name: 'newyork', temp: 30}
])
//=> [{"name": "california", "temp": 30}, {"name": "newyork", "temp": 30}]

这里假设传递给foldMap的列表是非空的。如果您可能会遇到一个空列表,那么您将需要进行相应的修改以返回某种默认值(如果没有默认值,则将其包装为Maybe类型)。

请参阅下面的完整代码段。

const MaxManyBy = fn => class MaxMany {
  constructor(values) {
    this.values = values
  }
  concat(other) {
    const otherValue = fn(other.values[0]),
          thisValue  = fn(this.values[0])

    return otherValue > thisValue ? other
         : otherValue < thisValue ? this
         : new MaxMany(this.values.concat(other.values))
  }
  static of(x) {
    return new MaxMany([x])
  }
}

const foldMap = (fn, [x, ...xs]) =>
  xs.reduce((acc, next) => acc.concat(fn(next)), fn(x))

const maxTemps = xs =>
  foldMap(MaxManyBy(({temp}) => temp).of, xs).values

const regions = [
  {name: 'california', temp: 30},
  {name: 'alabama', temp: 20},
  {name: 'newyork', temp: 30}
]

console.log(maxTemps(regions))