英文:
How do I convert a List[Option[(A, List[B])]] to the Option[(A,List[B])]? (Basically retrieve Option[X] from List[Option[X]])
问题
我有一个名为“rules”的元组列表,如下所示:
val rules = List[(A, List[B])],其中A和B是两个单独的case类
根据我的用例,我需要将其转换为一个`Option[(A, List[B])]`。A案例类包含一个类型为`Option[String]`的属性`id`,根据它返回元组。
我编写了一个名为`def findRule(entryId: Option[String])`的函数,我打算从中返回满足条件的元组`(A, List[B])`,即`A.id = entryId`作为一个Option。到目前为止,我编写了以下代码片段:
def findRule(entryId: Option[String]) = {
for {
ruleId <- rules.flatMap(_._1.id) // ruleId是一个String
id <- entryId // entryId是一个Option[String]
} yield {
rules.find(_ => ruleId.equalsIgnoreCase(id)) // 返回List[Option[(A, List[B])]]
}
}
这段代码返回一个`List[Option[(A, List[B])]`,但我不知道如何从中提取出Option。使用`.head()`不是一个选择,因为`rules`列表可能为空。请帮忙,我是Scala的新手。
示例(真实示例太大无法在此处发布,请考虑此代表性示例):
val rules = [(A = {id=1, ….}, B = [{B1}, {B2}, {B3}, …]), (A={id=2, ….}, B = [{B10}, {B11}, {B12}, …]), …. ](这里B并不重要,我需要根据case类A的id元素来找到元组)
现在,假设`entryId = Some(1)`
经过`findRule()`函数后,它将如下所示:
[Some((A = {id=1, …}, B = [{B1}, {B2}, {B3}, …]))]
我想返回:
Some((A = {id=1, …}, B = [{B1}, {B2}, {B3}, …])),即从`findRule()`返回的列表中获取Option(当前)。
英文:
I have a List of “rules”
tuples as follows:
val rules = List[(A, List[B])]
, where A
and B
are two separate case-classes
For the purposes of my use-case, I need to convert this to an Option[(A, List[B])]
. The A
case class contains a property id
which is of type Option[String]
, based on which the tuple is returned.
I have written a function def findRule(entryId: Option[String])
, from which I intend to return the tuple (A, List[B])
whose A.id = entryId
as an Option. So far, I have written the following snippet of code:
def findRule(entryId: Option[String]) = {
for {
ruleId <- rules.flatMap(_._1.id) // ruleId is a String
id <- entryId // entryId is an Option[String]
} yield {
rules.find(_ => ruleId.equalsIgnoreCase(id)) // returns List[Option[(A, List[B])]
}
}
This snippet returns a List[Option[(A, List[B])]
but I can’t figure out how to retrieve just the Option from it. Using .head()
isn’t an option, since the rules
list may be empty. Please help as I am new to Scala.
Example (the real examples are too large to post here, so please consider this representative example):
val rules = [(A = {id=1, ….}, B = [{B1}, {B2}, {B3}, …]), (A={id=2, ….}, B = [{B10}, {B11}, {B12}, …]), …. ]
(B
is not really important here, I need to find the tuple based on the id
element of case-class A
)
Now, suppose entryId = Some(1)
After the findRule()
function, this would currently look like:
[Some((A = {id=1, …}, B = [{B1}, {B2}, {B3}, …]))]
I want to return:
Some((A = {id=1, …}, B = [{B1}, {B2}, {B3}, …]))
, ie, the Option within the List returned (currently) from findRule()
答案1
得分: 2
根据你的问题,看起来你想基于某种条件从列表中选择一个单一的项目,这意味着你可能希望从 rules.find
开始。然后问题变成了如何表达传递给 find
的谓词函数。
根据我对你的问题的理解,条件是:
元组的
A
部分的id
需要与其他地方传递的entryId
匹配
def findRule(entryId: Option[String]) =
rules.find { case (a, listOfB) => entryId.contains(a.id) }
case
表达式只是处理元组的一种方便的语法。我也可以这样做:
rules.find { tup => entryId.contains(tup._1.id) }
Option
上的 contains
方法大致是"如果我是 Some,检查我的值是否等于参数;如果我是 None,只返回 false 并忽略参数"。它用于比较你对 entryId
的 Option[String]
和 A
的 id
的普通 String
。
你的第一个尝试没有成功,因为rules.flatMap
得到了一个 List
,在 for-理解中使用 <-
后继续使用它意味着另一个 flatMap
,这将保持它作为一个列表。
英文:
From your question, it sounds like you're trying to pick a single item from your list based on some conditional, which means you'll probably want to start with rules.find
. The problem then becomes how to express the predicate function that you pass to find
.
From my read of your question, the conditional is
> The id
on the A
part of the tuple needs to match the entryId
that was passed in elsewhere
def findRule(entryId: Option[String]) =
rules.find { case (a, listOfB) => entryId.contains(a.id) }
The case
expression is just nice syntax for dealing with the tuple. I could have also done
rules.find { tup => entryId.contains(tup._1.id) }
The contains
method on Option roughly does "if I'm a Some, see if my value equals the argument; if I'm a None, just return false and ignore the argument". It works as a comparison between the Option[String]
you have for entryId
and the plain String
you have for A
's id
.
Your first attempt didn't work because rules.flatMap
got you a List
, and using that after the <-
in the for-comprehension means another flatMap
, which keeps things as a List.
答案2
得分: 1
我个人更喜欢的一个变种是
```scala
def findRule(entryId: Option[String]): Option[(A, List[B])] = {
entryId.flatMap { id =>
rules.find { case (a, _) => a.id == id }
}
}
在entryId
为None
的情况下,它立即返回None
。如果你从rules.find
开始,即使entryId
为None
,它也会遍历所有规则并检查每一个。如果规则列表很小,可能不会有明显的性能差异,但我也觉得这种方式更直观易懂。
<details>
<summary>英文:</summary>
A variant that I personally prefer is
```scala
def findRule(entryId: Option[String]): Option[(A, List[B])] = {
entryId.flatMap { id =>
rules.find { case (a, _) => a.id == id }
}
}
In the case where entryId
is None
, it just immediately returns None
. If you start with rules.find
, then it will iterate through all of the rules and check each one even when entryId
is None
. It's unlikely to make any observable performance difference if the list of rules is small, but I also find it more intuitive to understand.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论