为啥从 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 不存在,它只是为了兼容怪异的 JavaJS,惯用的 Scala 代码从不使用null。并且 Arrays 也不鼓励,因为它们不是真正的集合而是平台原语,它们是可变和不变的,更喜欢 ListVector、ArraySeq 或任何真正的 immutable 集合。 @LuisMiguelMejíaSuárez 谢谢你的帮助,对 Scala 来说还是很新,所以我会看看 Type 的事情。我希望避免大量额外的代码,但我会尝试为问题添加一些上下文。 大家很抱歉 JS 有错别字 对不起,如果它更复杂,我想我不应该使用简单的字符串示例。 @JGleason 你能给我们一个BaseObjectdoSomething 的基本定义吗?此外,如果您也可以将它们更改为更惯用的替代方案,或者它们是否来自外部来源,那将是一件好事。 - 另外,您有时使用array 和其他collection,不确定是拼写错误还是更改了代码。 【参考方案1】:

如果您想累积 Strings 而不是 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 将返回一个与集合中包含的值相同类型的值。因此,如果您有一个 StringsArray,那么 reduce 的结果将是一个 String (哦,好吧,任何超类型String,这就是为什么在这种情况下会出现奇怪的 Serializablereduce 有一个更通用的版本,它允许您提供任何类型作为输出,即需要初始值的 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 似乎不合适的主要内容,如果未能解决你的问题,请参考以下文章

为啥从Vue.js中的列表中删除项目时移动转换需要绝对位置

js中使用reduce将json数组 转换为json

为啥在java 8中转换类型的reduce方法需要一个组合器

javascript中的map和reduce

Scala 学习之 aggregate函数

为啥我的 hadoop map reduce 程序中出现类转换异常?