为啥从 JS 转换为 Scala 时 reduce 似乎不合适
Posted
技术标签:
【中文标题】为啥从 JS 转换为 Scala 时 reduce 似乎不合适【英文标题】:Why doesn't reduce seem to fit when converting from JS to Scala为什么从 JS 转换为 Scala 时 reduce 似乎不合适 【发布时间】:2020-12-09 21:52:56 【问题描述】:我有以下 JS 代码...
const collection=["Thing1","Thing2"];
const doSomething = (value)=>
switch(value)
case "Thing1":
return
arrayItems: ["Object 1", "Object 2"]
break;
default:
return
arrayItems: ["Object 3", "Object 4"]
const result = collection.reduce(
(result, value)=> result.concat(doSomething(value).arrayItems),
[]
);
console.log(result);
// doSomething returns "arrayItems": ["Object 1", "Object 2"] for Thing1
// and "arrayItems": ["Object 3", "Object 4"] for Thing 2
// result should be ["Object 1", "Object 2","Object 3", "Object 4"]
jsfiddle
我现在想把它变成类似这样的 Scala reduce...
val collection = ["Thing 1", "Thing 2"]
val result: Array[...] = collection
.reduce(
(array: Array[...], value: String) =>
val objectList = service.doSomething(value)
if (array != null && array.length > 0)
array.concat(objectList)
else
objectList
);
// doSomething returns "arrayItems": ["Object 1", "Object 2"] for Thing1
// and "arrayItems": ["Object 3", "Object 4"] for Thing 2
// result should be ["Object 1", "Object 2","Object 3", "Object 4"]
但是当我尝试这个时,我得到......
type mismatch;
found : (Array[...], String) => Array[...]
required: (java.io.Serializable, java.io.Serializable) => java.io.Serializable
val result: Array[...] = doSomething(value).reduce((array: Array[...], value: String)=>
有没有办法做到这一点?
更新
我被要求提供我想要完成的工作的背景,所以我想要完成的基本步骤是......
-
获取字符串数组
对生成对象集合的字符串运行 reduce 函数
在 reduce 函数中,我们使用字符串调用提供对象集合的服务
然后将这些对象与 reduce 值连接并返回
每个答案的更新
谢谢大家,在你提出之前我不知道 foldLeft 并且仍然有点理解。首先是我试图避免的工作循环......
var array : Array[BaseObject] = Array[BaseObject]()
collection.foreach((value: String) =>
val objectList = doSomething(value).arrayItems
array = array.concat(objectList)
);
我试过了……
val array: List[BaseObject] =
collection.foldLeft(List.empty[BaseObject])
(myList, value) =>
val list : List[BaseObject] = List.from(doSomething(value).arrayList)
myList ++ list
;
但肯定有一些小问题,因为当我使用 gson toJson
解析时,我得到......
"head":...,"next":
head 是 Objects 之一,但不是整个集合。
【问题讨论】:
如果你能解释你想要做什么并输入完整的代码可能会有所帮助(例如Arrays里面的内容) - 另外,在 Scala 中,你可以假设null
不存在,它只是为了兼容怪异的 Java 和 JS,惯用的 Scala 代码从不使用null
。并且 Arrays 也不鼓励,因为它们不是真正的集合而是平台原语,它们是可变和不变的,更喜欢 List、Vector、ArraySeq 或任何真正的 immutable 集合。
@LuisMiguelMejíaSuárez 谢谢你的帮助,对 Scala 来说还是很新,所以我会看看 Type 的事情。我希望避免大量额外的代码,但我会尝试为问题添加一些上下文。
大家很抱歉 JS 有错别字
对不起,如果它更复杂,我想我不应该使用简单的字符串示例。
@JGleason 你能给我们一个BaseObject
和doSomething
的基本定义吗?此外,如果您也可以将它们更改为更惯用的替代方案,或者它们是否来自外部来源,那将是一件好事。 - 另外,您有时使用array
和其他collection
,不确定是拼写错误还是更改了代码。
【参考方案1】:
如果您想累积 String
s 而不是 List
,请优先使用 FoldLeft。
我假设您的...
在以下代码中是String
:
val result: List[String] = myCollection.foldLeft(List.empty[String]) (myList, value) =>
val newObject = service.doSomething(value)
myList :+ newObject
;
还有:
更喜欢使用不可变集合而不是可变集合。 强烈反对在 Scala 中使用null
。如果您的 List 为空,则甚至不会执行。
你也可以使用占位符来缩短你的代码,会逐渐变成:
val result: List[String] = myCollection.foldLeft(List.empty[String]) (myList, value) =>
myList :+ service.doSomething(value)
;
然后用占位符
val result: List[String] = myCollection.foldLeft(List.empty[String]) (_, _) => _ :+ service.doSomething(_)
;
【讨论】:
谢谢,我还是新手,所以我会看看这个,但这看起来真的很好。仅供参考,当我制作我的 jsfiddle 时,我注意到有一个错字,但我认为你有这个想法。 向左折叠看起来会起作用,但我原来的例子有点太简单了。 我使用了 FoldLeft,因为你的 reduce 中的类型似乎不同。如果它是相同的类型,例如在串联中,它会起作用。您调用查看 scaladoc 中 reduce 和 foldleft 的签名:scala-lang.org/api/current/scala/collection/immutable/… 没问题我不清楚,因为我还不确定 Scala 中到底什么是重要的。所以这是我的错,非常感谢你的帮助【参考方案2】:主要问题是reduce
将返回一个与集合中包含的值相同类型的值。因此,如果您有一个 Strings 的 Array,那么 reduce
的结果将是一个 String (哦,好吧,任何超类型String,这就是为什么在这种情况下会出现奇怪的 Serializable)。reduce
有一个更通用的版本,它允许您提供任何类型作为输出,即需要初始值的 foldLeft
。
使用 Lists 而不是 Arrays 我们可以这样写:
def concat(input: List[String])(f: String => List[String]): List[String] =
input.foldLeft(List.empty[String])
case (acc, str) => acc ++ f(str)
然而,追加到List的效率有点低,所以我们可以这样重写它来提高性能。 我们将前置所有结果,然后我们将反转列表以按预期顺序获取输出:
def concat(input: List[String])(f: String => List[String]): List[String] =
input.foldLeft(List.empty[String])
case (acc, str) => f(str) reverse_::: acc
.reverse
但是,这是一个非常普遍的操作。我们有一个A
类型的集合和一个返回某种B
类型集合的函数,我们希望将该函数应用于原始集合的所有元素,并将结果分组为一个B
类型的集合。
这是flatMap
的工作(顺便说一句,它更通用;因为它适用于任何Monad,而不仅仅是集合)。
def concat(input: List[String])(f: String => List[String]): List[String] =
input.flatMap(f)
我建议您查看 Scaladoc 并遵循一些教程,以了解有关 stdlib 提供的所有操作的更多信息。 另外,请不要犹豫,提出任何问题。
可以看到运行here的代码。
【讨论】:
谢谢你的想法甚至让我大吃一惊,所以我可能需要一点时间才能完成它。我尝试发布我的第一个不成功的 leftFold 试用版。以上是关于为啥从 JS 转换为 Scala 时 reduce 似乎不合适的主要内容,如果未能解决你的问题,请参考以下文章