首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >换能器扁平和uniq

换能器扁平和uniq
EN

Stack Overflow用户
提问于 2018-08-24 16:20:49
回答 2查看 607关注 0票数 3

我想知道是否有一种方法,通过使用传感器来扁平一个列表,并对唯一的值进行过滤?

通过链接,这是非常容易的:

代码语言:javascript
复制
import {uniq, flattenDeep} from 'lodash';|

const arr = [1, 2, [2, 3], [1, [4, 5]]];

uniq(flattendDeep(arr)); // ->  [1, 2, 3, 4, 5]
代码语言:javascript
复制
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.core.min.js"></script>

但在这里,我们循环了两次列表(+ n按深度层)。不太理想。

我想要达到的目的是在这个案子里用一个传感器。我已经阅读了关于https://ramdajs.com/docs/#transduce的Ramda文档,但是我仍然找不到正确编写它的方法。

目前,我使用了一个包含递归函数的约简函数:

代码语言:javascript
复制
import {isArray} from 'lodash';

const arr = [1, 2, [2, 3], [1, [4, 5]]];

const flattenDeepUniq = (p, c) => {
    if (isArray(c)) {
        c.forEach(o => p = flattenDeepUniq(p, o));
    }
    else {
        p = !p.includes(c) ? [...p, c] : p;
    }

    return p;
};

arr.reduce(flattenDeepUniq, []) // -> [1, 2, 3, 4, 5]
代码语言:javascript
复制
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.core.min.js"></script>

我们在元素上有一个循环(+ n个有深度层的循环),这看起来更好,更优化。

在这种情况下,甚至可以使用换能器和迭代器吗?有关Ramda转换功能的更多信息:https://gist.github.com/craigdallimore/8b5b9d9e445bfa1e383c569e458c3e26

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-08-24 19:31:06

换能器在这里没什么意义。您的数据结构是递归的。处理递归结构的最佳代码通常需要递归算法。

传感器是如何工作的

(罗曼·利蒂科夫(Roman )给传感器写了一个不错的游戏攻略。)

传感器都是关于用单个数据替换通过同一数据的多次行程,将步骤的原子操作组合成一个操作。

换能器很适合于转换此代码:

代码语言:javascript
复制
xs.map(x => x * 7).map(x => x + 3).filter(isOdd(x)).take(5)
//  ^               ^                 ^               ^
//   \               \                 \               `------ Iteration 4
//    \               \                 `--------------------- Iteration 3
//     \               `-------------------------------------- Iteration 2
//      `----------------------------------------------------- Iteration 1

变成这样的东西:

代码语言:javascript
复制
xs.reduce((r, x) => r.length >= 5 ? res : isOdd(x * 7 + 3) ? res.concat(x * 7 - 3) : res, [])
//    ^
//     `------------------------------------------------------- Just one iteration

在兰达,因为mapfiltertake都启用了换能器,所以我们可以转换。

代码语言:javascript
复制
const foo = pipe(
  map(multiply(7)), 
  map(add(3)), 
  filter(isOdd), 
  take(3)
)

foo([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) //=> [17, 31, 45]

(在数据中迭代四次)

代码语言:javascript
复制
const bar = compose(
  map(multiply(7)), 
  map(add(3)), 
  filter(isOdd), 
  take(3)
)

into([], bar, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])  //=> [17, 31, 45]

只重复一次。(注意从pipe切换到compose。换能器按与普通函数相反的顺序组成)。

注意,这种换能器的关键点是它们的操作都是相似的。map将列表转换为另一个列表,filtertake也是如此。虽然您可以拥有在不同类型上工作的传感器,而mapfilter也可能在这种类型上工作,但只有当您将在同一类型上操作的函数组合在一起时,它们才能一起工作。

Flatten是换能器的弱匹配

你的结构更复杂。虽然我们当然可以创建一个函数,以某种方式(预顺序、后置顺序)来爬行它,因此它可能会启动换能器管道,但是处理递归结构的逻辑方法是使用递归算法。

扁平这样一个嵌套结构的简单方法如下所示:

代码语言:javascript
复制
const flatten = xs => xs.reduce(
  (a, x) => concat(a, isArray(x) ? flatten(x) : [x]), 
  []
);

(出于各种技术原因,Ramda的代码要复杂得多。)

然而,这个递归版本并不适合与传感器一起工作,实际上,传感器必须一步一步地工作。

Uniq不适合换能器

另一方面,uniq用这样的换能器就没什么意义了。问题是,uniq使用的容器,如果要从传感器中获得任何好处,必须是具有快速插入和快速查找的容器,最有可能是SetObject。假设我们使用Set。那么我们就有了一个问题,因为我们的flatten对列表进行操作。

另一种方法

由于我们不能轻松地将现有的函数折叠成您想要的功能,所以我们可能需要一次性编写。

早期解决方案的结构使得添加唯一性约束变得相当容易。同样,这是:

代码语言:javascript
复制
const flatten = xs => xs.reduce(
  (a, x) => concat(a, isArray(x) ? flatten(x) : [x]), 
  []
);

使用帮助函数将所有元素添加到Set中。

代码语言:javascript
复制
const addAll = (set, xs) => xs.reduce((s, x) => s.add(x), set)

我们可以编写一个扁平的函数,只保留唯一的值:

代码语言:javascript
复制
const flattenUniq = xs => xs.reduce(
  (s, x) => addAll(s, isArray(x) ? flattenUniq(x) : [x]), 
  new Set()
)

请注意,这与上面的结构非常相似,它只使用Set,因此从concat切换到我们的addAll

当然,您可能需要一个数组,在最后。我们只需用一个Set -> Array函数包装就可以做到这一点,如下所示:

代码语言:javascript
复制
const flattenUniq = xs => Array.from(xs.reduce(
  (s, x) => addAll(s, isArray(x) ? flattenUniq(x) : [x]), 
  new Set()
))

您还可以考虑将此结果保留为Set。如果您真的想要一个唯一值的集合,那么Set是合乎逻辑的选择。

这样的函数不像无点转换函数那样优雅,但是它可以工作,公开的管道使得与原始数据结构和普通flatten函数之间的关系更加清晰。

我想你可以把这个长长的答案看作是指出user633183在评论中说的话的一种长篇大论的方式:“扁平或uniq都不是换能器的好用例。”

票数 7
EN

Stack Overflow用户

发布于 2022-06-11 17:10:23

Uniq现在是Ramda的换能器,所以你可以直接使用它。至于扁平化,您可以在前面的树上遍历,生成一组平面值。

代码语言:javascript
复制
const arr = [1, 2, [2, 3], [1, [4, 5]]];

const deepIterate = function*(list) {
  for (const it of list) {
    yield* Array.isArray(it) ? deepIterate(it) : [it];
  }
}

R.into([], R.uniq(), deepIterate(arr)) // ->  [1, 2, 3, 4, 5]

这使您可以合成额外的传感器。

代码语言:javascript
复制
R.into([], R.compose(R.uniq(), R.filter(isOdd), R.take(5)), deepIterate(arr))
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52008364

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档