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

com.github.tonivade.purefun.data.Sequence Maven / Gradle / Ivy

/*
 * Copyright (c) 2018-2019, Antonio Gabriel Muñoz Conejo 
 * Distributed under the terms of the MIT License
 */
package com.github.tonivade.purefun.data;

import static java.util.Objects.requireNonNull;
import static java.util.Spliterators.spliteratorUnknownSize;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Stream.iterate;

import java.util.Iterator;
import java.util.Spliterator;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import com.github.tonivade.purefun.Function1;
import com.github.tonivade.purefun.Function2;
import com.github.tonivade.purefun.HigherKind;
import com.github.tonivade.purefun.Matcher1;
import com.github.tonivade.purefun.Operator2;
import com.github.tonivade.purefun.PartialFunction1;
import com.github.tonivade.purefun.Tuple;
import com.github.tonivade.purefun.Tuple2;
import com.github.tonivade.purefun.type.Option;

@HigherKind
public interface Sequence extends Iterable {

  int size();

  boolean contains(E element);
  Sequence append(E element);
  Sequence remove(E element);
  Sequence appendAll(Sequence other);
  Sequence removeAll(Sequence other);

  Sequence reverse();

   Sequence map(Function1 mapper);

   Sequence flatMap(Function1> mapper);

  Sequence filter(Matcher1 matcher);

  default Sequence filterNot(Matcher1 matcher) {
    return filter(matcher.negate());
  }

  default Option reduce(Operator2 operator) {
    return Option.from(stream().reduce(operator::apply));
  }

  default E fold(E initial, Operator2 operator) {
    return stream().reduce(initial, operator::apply);
  }

  default  U foldLeft(U initial, Function2 combinator) {
    U accumulator = initial;
    for (E element : this) {
      accumulator = combinator.apply(accumulator, element);
    }
    return accumulator;
  }

  default  U foldRight(U initial, Function2 combinator) {
    return reverse().foldLeft(initial, (acc, e) -> combinator.apply(e, acc));
  }

  default  Sequence collect(PartialFunction1 function) {
    return filter(function::isDefinedAt).map(function::apply);
  }

  default  ImmutableMap> groupBy(Function1 selector) {
    return ImmutableMap.from(stream().collect(groupingBy(selector::apply))).mapValues(ImmutableList::from);
  }

  default ImmutableList asList() {
    return ImmutableList.from(stream());
  }

  default ImmutableArray asArray() {
    return ImmutableArray.from(stream());
  }

  default ImmutableSet asSet() {
    return ImmutableSet.from(stream());
  }

  default ImmutableTree asTree() {
    return ImmutableTree.from(stream());
  }

  default Stream stream() {
    return StreamSupport.stream(spliterator(), false);
  }

  default boolean isEmpty() {
    return size() == 0;
  }

  default Stream> zipWithIndex() {
    return zip(iterate(0, i -> i + 1), stream());
  }

  @SafeVarargs
  static  ImmutableArray arrayOf(E... elements) {
    return ImmutableArray.of(elements);
  }

  @SafeVarargs
  static  ImmutableList listOf(E... elements) {
    return ImmutableList.of(elements);
  }

  @SafeVarargs
  static  ImmutableSet setOf(E... elements) {
    return ImmutableSet.of(elements);
  }

  @SafeVarargs
  static > ImmutableTree treeOf(E... elements) {
    return ImmutableTree.of(elements);
  }

  static  Stream> zip(Iterator first, Iterator second) {
    return asStream(new PairIterator<>(first, second));
  }

  static  Stream> zip(Stream first, Stream second) {
    return zip(first.iterator(), second.iterator());
  }

  static  Stream> zip(Sequence first, Sequence second) {
    return zip(first.stream(), second.stream());
  }

  static  Stream> zipWithIndex(Stream stream) {
    return zip(stream, iterate(0, x -> x + 1));
  }

  static  Stream> zipWithIndex(Sequence sequence) {
    return zipWithIndex(sequence.stream());
  }

  static  Stream asStream(Iterator iterator) {
    return StreamSupport.stream(spliteratorUnknownSize(iterator, Spliterator.ORDERED), false);
  }
}

final class PairIterator implements Iterator> {

  private final Iterator first;
  private final Iterator second;

  PairIterator(Iterator first, Iterator second) {
    this.first = requireNonNull(first);
    this.second = requireNonNull(second);
  }

  @Override
  public boolean hasNext() {
    return first.hasNext() && second.hasNext();
  }

  @Override
  public Tuple2 next() {
    return Tuple.of(first.next(), second.next());
  }
}