scala - una - formula para buscar datos repetidos en excel
¿Cómo eliminar 2 o más duplicados de la lista y mantener su orden inicial? (13)
Aquí está el código canónico de Scala para reducir tres o más en una fila a dos en una fila:
def checkForTwo(candidate: List[Int]): List[Int] = {
candidate match {
case x :: y :: z :: tail if x == y && y == z =>
checkForTwo(y :: z :: tail)
case x :: tail =>
x :: checkForTwo(tail)
case Nil =>
Nil
}
}
Observa los primeros tres elementos de la lista y, si son iguales, elimina el primero y repite el proceso. De lo contrario, pasa elementos a través.
Asumamos que tenemos una lista de Scala:
val l1 = List(1, 2, 3, 1, 1, 3, 2, 5, 1)
Podemos eliminar fácilmente los duplicados utilizando el siguiente código:
l1.distinct
o
l1.toSet.toList
Pero, ¿qué pasa si queremos eliminar duplicados solo si hay más de 2? Entonces, si hay más de 2 elementos con el mismo valor, solo quedamos dos y eliminamos el resto.
Pude lograrlo con el siguiente código:
l1.groupBy(identity).mapValues(_.take(2)).values.toList.flatten
Eso me dio el resultado:
List(2, 2, 5, 1, 1, 3, 3)
Los elementos se eliminan, pero el orden de los elementos restantes es diferente de cómo aparecieron estos elementos en la lista inicial. ¿Cómo hacer esta operación y seguir siendo el orden de la lista original?
Entonces el resultado para l1 debería ser:
List(1, 2, 3, 1, 3, 2, 5)
Aquí hay una solución recursiva (se acumulará el desbordamiento de listas grandes):
def filterAfter[T](l: List[T], max: Int): List[T] = {
require(max > 1)
//keep the state of seen values
val seen = Map[T, Int]().withDefaultValue(0)//init to 0
def filterAfter(l: List[T], seen: Map[T, Int]): (List[T], Map[T, Int]) = {
l match {
case x :: xs =>
if (seen(x) < max) {
//Update the state and pass to next
val pair = filterAfter(xs, seen updated (x, seen(x) + 1))
(x::pair._1, pair._2)
} else {
//already seen more than max
filterAfter(xs, seen)
}
case _ => (l, seen)//empty, terminate recursion
}
}
//call inner recursive function
filterAfter(l, seen, 2)._1
}
Basado en la respuesta de experquisite, pero usando foldLeft
:
def noMoreThanBis(xs: List[Int], max: Int) = {
val initialState: (Map[Int, Int], List[Int]) = (Map().withDefaultValue(0), Nil)
val (_, result) = xs.foldLeft(initialState) { case ((count, res), x) =>
if (count(x) >= max)
(count, res)
else
(count.updated(x, count(x) + 1), x :: res)
}
result.reverse
}
De manera similar a como se implementa distinto, con un conjunto múltiple en lugar de un conjunto:
def noMoreThan[T](list : List[T], max : Int) = {
val b = List.newBuilder[T]
val seen = collection.mutable.Map[T,Int]().withDefaultValue(0)
for (x <- list) {
if (seen(x) < max) {
b += x
seen(x) += 1
}
}
b.result()
}
Es un poco feo, pero es relativamente rápido
val l1 = List(1, 2, 3, 1, 1, 3, 2, 5, 1)
l1.foldLeft((Map[Int, Int](), List[Int]())) { case ((m, ls), x) => {
val z = m + ((x, m.getOrElse(x, 0) + 1))
(z, if (z(x) <= 2) x :: ls else ls)
}}._2.reverse
Da: List(1, 2, 3, 1, 3, 2, 5)
Mi humilde respuesta:
def distinctOrder[A](x:List[A]):List[A] = {
@scala.annotation.tailrec
def distinctOrderRec(list: List[A], covered: List[A]): List[A] = {
(list, covered) match {
case (Nil, _) => covered.reverse
case (lst, c) if c.count(_ == lst.head) >= 2 => distinctOrderRec(list.tail, covered)
case _ => distinctOrderRec(list.tail, list.head :: covered)
}
}
distinctOrderRec(x, Nil)
}
Con los resultados:
scala> val l1 = List(1, 2, 3, 1, 1, 3, 2, 5, 1)
l1: List[Int] = List(1, 2, 3, 1, 1, 3, 2, 5, 1)
scala> distinctOrder(l1)
res1: List[Int] = List(1, 2, 3, 1, 3, 2, 5)
En la Edición: ¡Justo antes de irme a la cama, se me ocurrió esto!
l1.foldLeft(List[Int]())((total, next) => if (total.count(_ == next) >= 2) total else total :+ next)
Con una respuesta de:
res9: List[Int] = List(1, 2, 3, 1, 3, 2, 5)
No es el más eficiente.
scala> val l1 = List(1, 2, 3, 1, 1, 3, 2, 5, 1)
l1: List[Int] = List(1, 2, 3, 1, 1, 3, 2, 5, 1)
scala> l1.zipWithIndex.groupBy( _._1 ).map(_._2.take(2)).flatten.toList.sortBy(_._2).unzip._1
res10: List[Int] = List(1, 2, 3, 1, 3, 2, 5)
No es la más bonita. Espero ver las otras soluciones.
def noMoreThan(xs: List[Int], max: Int) =
{
def op(m: Map[Int, Int], a: Int) = {
m updated (a, m(a) + 1)
}
xs.scanLeft( Map[Int,Int]().withDefaultValue(0) ) (op).tail
.zip(xs)
.filter{ case (m, a) => m(a) <= max }
.map(_._2)
}
scala> noMoreThan(l1, 2)
res0: List[Int] = List(1, 2, 3, 1, 3, 2, 5)
Prefiero acercarme con Map
inmutable:
def noMoreThan[T](list: List[T], max: Int): List[T] = {
def go(tail: List[T], freq: Map[T, Int]): List[T] = {
tail match {
case h :: t =>
if (freq(h) < max)
h :: go(t, freq + (h -> (freq(h) + 1)))
else go(t, freq)
case _ => Nil
}
}
go(list, Map[T, Int]().withDefaultValue(0))
}
Qué tal esto:
list
.zipWithIndex
.groupBy(_._1)
.toSeq
.flatMap { _._2.take(2) }
.sortBy(_._2)
.map(_._1)
Solución con groupBy
y filter, sin ningún tipo de clasificación (por lo que es O (N), la clasificación le dará O (Nlog (N)) adicional en un caso típico):
val li = l1.zipWithIndex
val pred = li.groupBy(_._1).flatMap(_._2.lift(1)) //1 is your "2", but - 1
for ((x, i) <- li if !pred.get(x).exists(_ < i)) yield x
Versión más sencilla utilizando foldLeft:
l1.foldLeft(List[Int]()){(acc, el) =>
if (acc.count(_ == el) >= 2) acc else el::acc}.reverse
distinct
se define para SeqLike
como
/** Builds a new $coll from this $coll without any duplicate elements.
* $willNotTerminateInf
*
* @return A new $coll which contains the first occurrence of every element of this $coll.
*/
def distinct: Repr = {
val b = newBuilder
val seen = mutable.HashSet[A]()
for (x <- this) {
if (!seen(x)) {
b += x
seen += x
}
}
b.result()
}
Podemos definir nuestra función de manera muy similar:
def distinct2[A](ls: List[A]): List[A] = {
val b = List.newBuilder[A]
val seen1 = mutable.HashSet[A]()
val seen2 = mutable.HashSet[A]()
for (x <- ls) {
if (!seen2(x)) {
b += x
if (!seen1(x)) {
seen1 += x
} else {
seen2 += x
}
}
}
b.result()
}
scala> distinct2(l1)
res4: List[Int] = List(1, 2, 3, 1, 3, 2, 5)
Esta versión usa el estado interno, pero sigue siendo puro. También es bastante fácil generalizar para n arbitraria (actualmente 2), pero la versión específica es más eficaz.
Puede implementar la misma función con los pliegues que llevan el estado "lo que se ve una y dos veces" con usted. Sin embargo, el bucle for y el estado mutable hacen el mismo trabajo.