All Downloads are FREE. Search and download functionalities are using the official Maven repository.

swaydb.core.util.Collections.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2020 Simer JS Plaha ([email protected] - @simerplaha)
 *
 * This file is a part of SwayDB.
 *
 * SwayDB is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * SwayDB is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with SwayDB. If not, see .
 *
 * Additional permission under the GNU Affero GPL version 3 section 7:
 * If you modify this Program or any covered work, only by linking or combining
 * it with separate works, the licensors of this Program grant you additional
 * permission to convey the resulting work.
 */

package swaydb.core.util

import swaydb.data.slice.Slice

import scala.reflect.ClassTag

private[swaydb] object Collections {

  val emptyStringSeq = Seq.empty[String]

  implicit class IterableImplicit[T](items: Iterable[T]) {

    @inline final def foreachBreak(f: T => Boolean): Unit = {
      val iterator = items.iterator
      var break: Boolean = false
      while (iterator.hasNext && !break)
        break = f(iterator.next())
    }

    /**
     * Used for cases when multiple iterators over a list eg: collect & then fold is costly.
     */
    @inline final def foldLeftWhile[B](initial: B, condition: T => Boolean)(op: (B, T) => B): B = {
      var result = initial
      items foreachBreak {
        item =>
          val continue = condition(item)
          if (continue) result = op(result, item)
          !continue
      }
      result
    }

    @inline final def untilSome[R](f: T => Option[R]): Option[(R, T)] = {
      items foreach {
        item =>
          f(item) match {
            case Some(value) =>
              //Not a good idea to break out with return. Needs improvement.
              return Some((value, item))

            case None =>
            //continue reading
          }
      }
      None
    }
  }

  /**
   * Groups items ensuring if the input groupSize is > 1 then the output groups
   * should not contain a single item. Single items value merged into their previous gorup.
   */
  def groupedMergeSingles[T](groupSize: Int,
                             items: List[T]): List[List[T]] =
    if (groupSize <= 0) {
      List(items)
    } else {
      val grouped: List[List[T]] = items.grouped(groupSize).toList
      if (groupSize > 1 && grouped.size >= 2 && grouped.last.size == 1)
        grouped.dropRight(2) :+ (grouped(grouped.size - 2) ++ grouped.last)
      else
        grouped
    }

  /**
   * Groups items ensuring if the input groupSize is > 1 then the output groups
   * should not contain a single item. Single items value merged into their previous gorup.
   */
  def groupedMergeSingles[T](groupSize: Int,
                             items: List[T],
                             splitAt: Int): List[List[T]] = {
    val (itemsToGroupHead, itemsToGroupLast) = items.splitAt(splitAt)

    groupedMergeSingles(groupSize, itemsToGroupHead) ++
      groupedMergeSingles(groupSize, itemsToGroupLast)
  }

  def groupedBySize[T: ClassTag](minGroupSize: Int,
                                 itemSize: T => Int,
                                 items: Slice[T]): Slice[Slice[T]] =
    if (minGroupSize <= 0) {
      Slice(items)
    } else {
      val allGroups =
        Slice
          .of[Slice[T]](items.size)
          .add(Slice.of[T](items.size))

      var currentGroupSize = 0

      var i = 0
      while (i < items.size) {
        if (currentGroupSize < minGroupSize) {
          val currentItem = items(i)
          allGroups.last add currentItem
          currentGroupSize += itemSize(currentItem)
          i += 1
        } else {
          val tailItemsSize = items.drop(i).foldLeft(0)(_ + itemSize(_))
          if (tailItemsSize >= minGroupSize) {
            val newGroup = Slice.of[T](items.size - i + 1)
            allGroups add newGroup
            currentGroupSize = 0
          } else {
            currentGroupSize = minGroupSize - 1
          }
        }
      }

      allGroups
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy