2012-05-30 8 views
1

Я работаю над методом, который имеет 3 возможных результата для нескольких элементов: Ошибка, Неверно и Успех. Для каждого из них мне нужно вернуть список json, определяющий, какие элементы были ошибочными, недействительными и успешными.Возврат нескольких коллекций из flatMap

Моя текущая попытка. Я использовал Object, чтобы представить класс, который мои объекты полностью объясняют, займет слишком много времени. Object класс имеет метод process, который возвращает логическое значение, чтобы указать успех или ошибку и генерирует исключение, если объект является недействительным:

def process(list: List[Objects]) = { 
    val successIds = new ListBuffer[Int](); 
    val errorIds = new ListBuffer[Int](); 
    val invalidIds = new ListBuffer[Int](); 

    list.foreach(item => { 
     try { 
      if (item.process) { 
       successIds ++ item.id 
      } else { 
       errorIds ++ item.id 
      } 
     } catch { 
      case e: Exception => invalidIds ++ item.id 
     } 
    }) 

    JsonResult(
     Map("success" -> successIds, 
      "failed" -> errorIds, 
      "invalid" -> invalidIds) 
    ) 
} 

Проблема использует Мутабельном структуры данных не очень «Скала-у». Я предпочел бы строить эти списки более функциональным образом, но я совершенно новый для scala. Любые мысли или подсказки о том, как это можно сделать?

Мой, хотя использует что-то вроде метода flatMap, который принимает кортеж из коллекций и сопоставляет их таким же образом, метод flatMap делает для одной коллекции:

def process(list: List[Objects]) = { 

    val (success, error, invalid) = list.flatMap(item => { 
     try { 
      if (item.process) { 
       (List(item.id), List.empty, List.empty) 
      } else { 
       (List.empty, List(item.id), List.empty) 
      } 
     } catch { 
      case e: Exception => 
       (List.empty, List.empty, List(item.id)) 
     } 
    }) 

    JsonResult(
     Map("success" -> success, 
      "failed" -> error, 
      "invalid" -> invalid) 
    ) 
} 
+0

Отметьте этот Gist, который объясняет использование аппликативных функторов для безотказной проверки, любезно предоставленной ScalaZ: https://gist.github.com/970717 – opyate

ответ

8

flatMap не то, что вам нужно здесь - Вам нужно groupBy:

def process(list: List[Objects]) = { 

    def result(x: Objects) = 
    try if (x.process) "success" else "failed" 
    catch {case _ => "invalid"}  

    JsonResult(list groupBy result mapValues (_ map (_.id))) 
} 
+0

+1 - определенно лучше для этой конкретной проблемы, чем мой ответ ! –

+0

+1, «попробуйте, если ... еще ...», есть ли что-нибудь, что этот язык не может сделать? – virtualeyes

+0

Взял меня, чтобы на самом деле реализовать это, извините. Красиво сделанный, простой и лаконичный +1 –

1

Там всегда рекурсии:

class Ob(val id: Int) { def okay: Boolean = id < 5 } 


@annotation.tailrec def process(
    xs: List[Ob], 
    succ: List[Int] = Nil, 
    fail: List[Int] = Nil, 
    invalid: List[Int] = Nil 
): (List[Int], List[Int], List[Int]) = xs match { 
    case Nil => (succ.reverse, fail.reverse, invalid.reverse) 
    case x :: more => 
    val maybeOkay = try { Some(x.okay) } catch { case e: Exception => None } 
    if (!maybeOkay.isDefined) process(more, succ, fail, x.id :: invalid) 
    else if (maybeOkay.get) process(more, x.id :: succ, fail, invalid) 
    else      process(more, succ, x.id :: fail, invalid) 
} 

Который работает, как можно было бы надеяться (пропустить превратности, если вы не заботитесь о порядке):

scala> process(List(new Ob(1), new Ob(7), new Ob(2), 
    new Ob(4) { override def okay = throw new Exception("Broken") })) 

res2: (List[Int], List[Int], List[Int]) = (List(1,2),List(7),List(4)) 
0

адаптированный, чтобы сделать его компиляции без «Объекты»

def procex (item: String): Boolean = ((9/item.toInt) < 1) 

def process (list: List[String]) = { 
    val li: List[(Option[String], Option[String], Option[String])] = list.map (item => { 
     try { 
      if (procex (item)) { 
       (Some (item), None, None) 
      } else { 
       (None, Some (item), None) 
      } 
     } catch { 
      case e: Exception => 
       (None, None, Some (item)) 
     } 
    }) 
    li 
} 
// below 10 => failure 
val in = (5 to 15).map (""+_).toList 
// 0 to throw a little exception 
val ps = process ("0" :: in) 

val succeeders = ps.filter (p=> p._1 != None).map (p=>p._1) 
val errors  = ps.filter (p=> p._2 != None).map (p=>p._2) 
val invalides = ps.filter (p=> p._3 != None).map (p=>p._3) 

Что не работает :

(1 to 3).map (i=> ps.filter (p=> p._i != None).map (p=>p._i)) 

_i не работает.

Смежные вопросы