web-dev-qa-db-fra.com

Comment rendre implicite Ordered sur Java.time.LocalDate

Je veux utiliser Java.time.LocalDate et Java.time.LocalDateTime avec un Ordered implicite comme:

val date1 = Java.time.LocalDate.of(2000, 1, 1)
val date2 = Java.time.LocalDate.of(2010, 10, 10)
if (date1 < date2) ...

import scala.math.Ordering.Implicits._ ne fonctionne pas, car LocalDate hérite de Comparable<ChronoLocalDate> au lieu de Comparable<LocalDate>. Comment puis-je écrire mon propre Orderd imlicit pour utiliser <, <=,>,> = opérateurs/méthodes afin de comparer les LocalDate?

Modifier:

J'ai trouvé un moyen d'utiliser une classe implicite:

import Java.time.{LocalDate}

object MyDateTimeUtils {
  implicit class MyLocalDateImprovements(val ld: LocalDate)
           extends Ordered[LocalDate] {
    def compare(that: LocalDate): Int = ld.compareTo(that)
  }
}

// Test

import MyDateTimeUtils._

val d1 = LocalDate.of(2016, 1, 1)
val d2 = LocalDate.of(2017, 2, 3)

if (d1 < d2) println("d1 is less than d2")

Mais je préférerais une méthode comme celle de Scala pour toutes les classes Java qui implémente Comparable<T>. Il vous suffit de import scala.math.Ordering.Implicits._ dans votre code. Scala l'implémente comme:

implicit def ordered[A <% Comparable[A]]: Ordering[A] = new Ordering[A] {
  def compare(x: A, y: A): Int = x compareTo y
}

Malheureusement, LocalDate implémente Comparable<ChronoLocalDate> au lieu de Comparable<LocalDate>. Je ne pouvais pas trouver un moyen de modifier la méthode ordonnée implicite ci-dessus pour l'adapter à LocalDate/Comparable<ChronoLocalDate>. Une idée?

13
Michael

Vous pouvez utiliser Ordering.by pour créer un ordre pour n’importe quel type, en donnant une fonction de ce type à quelque chose qui a déjà un ordre - dans ce cas, à Long:

implicit val localDateOrdering: Ordering[LocalDate] = Ordering.by(_.toEpochDay)
15
Tzach Zohar

Comparer par LocalDate.toEpochDay est clair, bien que peut-être relativement lent ...

Le réponse de @ tzach-zohar est génial, en ce sens que ce qui se passe est plus évident; vous commandez par Epoch Day:

implicit val localDateOrdering: Ordering[LocalDate] = Ordering.by(_.toEpochDay)

Cependant, si vous regardez l’implémentation de toEpochDay , vous verrez qu’elle est relativement impliquée - 18 lignes de code, avec 4 divisions, 3 conditions et un appel à isLeapYear() - et la valeur résultante n’est pas mise en cache. Ainsi, il est recalculé à chaque comparaison, ce qui peut coûter cher s’il ya un grand nombre de LocalDates à trier.

... utiliser LocalDate.compareTo est probablement plus performant ...

Le implémentation de LocalDate.compareTo est plus simple - 2 conditions seulement, pas de division - et vous obtiendrez ce que vous obtiendriez avec cette conversion implicite de Java.lang.Comparable EN scala.math.Ordering que scala.math.Ordering.Implicits._ offre, si seulement cela fonctionnait ! Mais comme vous l'avez dit, ce n'est pas le cas, car LocalDate hérite de Comparable<ChronoLocalDate> au lieu de Comparable<LocalDate>. Une façon d'en tirer parti pourrait être ...

import scala.math.Ordering.Implicits._

implicit val localDateOrdering: Ordering[LocalDate] =
  Ordering.by(identity[ChronoLocalDate])

... qui vous permet de commander LocalDates en les convertissant en ChronoLocalDates, et en utilisant le Ordering[ChronoLocalDate] que scala.math.Ordering.Implicits._ vous donne!

... et en fin de compte, la syntaxe lambda pour les types SAM convient mieux

La syntaxe lambda pour les types SAM , introduite avec Scala 2.12, permet de créer un new Ordering très rapidement:

implicit val localDateOrdering: Ordering[LocalDate] = _ compareTo _

... et je pense que cela finit par être mon préféré! Concis, toujours assez clair, et utilisant (je pense) la méthode de comparaison la plus performante.

6
Roberto Tyley

Voici la solution que j'utilise:

définir deux implications. Le premier pour rendre un Ordering[LocalDate] disponible. Et une seconde pour donner à LocalDate une méthode compare qui s'avère très utile. En règle générale, je les mets dans des objets de package d'une bibliothèque. Je peux simplement les inclure où j'en ai besoin.

package object net.fosdal.oslo.odatetime {

  implicit val orderingLocalDate: Ordering[LocalDate] = Ordering.by(d => (d.getYear, d.getDayOfYear))

  implicit class LocalDateOps(private val localDate: LocalDate) extends AnyVal with Ordered[LocalDate] {
    override def compare(that: LocalDate): Int = Ordering[LocalDate].compare(localDate, that)
  }
}

avec les deux définis, vous pouvez maintenant faire des choses comme:

import net.fosdal.oslo.odatetime._

val bool: Boolean = localDate1 < localDate1

val localDates: Seq[LocalDate] = ...
val sortedSeq = localDates.sorted

Alternativement ... vous pouvez simplement utiliser ma bibliothèque (v0.4.3) directement. voir: https://github.com/sfosdal/oslo

2
sfosdal

Une légère modification de la variable ordered implicite devrait suffire.

type AsComparable[A] = A => Comparable[_ >: A]

implicit def ordered[A: AsComparable]: Ordering[A] = new Ordering[A] {
  def compare(x: A, y: A): Int = x compareTo y
}

Maintenant, tout type qui est comparable à lui-même ou à un supertype de lui-même devrait avoir une Ordering.

0
Jasper-M