【问题标题】: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)=>{

有没有办法做到这一点?

更新

我被要求提供我想要完成的工作的背景,所以我想要完成的基本步骤是......

  1. 获取字符串数组
  2. 对生成对象集合的字符串运行 reduce 函数
  3. 在 reduce 函数中,我们使用字符串调用提供对象集合的服务
  4. 然后将这些对象与 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,不确定是拼写错误还是更改了代码。

标签: javascript scala reduce


【解决方案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,这就是为什么在这种情况下会出现奇怪的 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 试用版。
猜你喜欢
  • 2011-02-03
  • 2010-12-23
  • 2013-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-27
  • 1970-01-01
相关资源
最近更新 更多