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

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

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

import static com.github.tonivade.purefun.core.Precondition.checkNonNull;
import static java.util.Spliterators.spliteratorUnknownSize;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.joining;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.SequencedCollection;
import java.util.Spliterator;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

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

@HigherKind
public non-sealed interface Sequence extends SequenceOf, Iterable, Bindable, E> {

  int size();

  boolean contains(Object element);

  default boolean containsAll(Iterable elements) {
    for (var e : elements) {
      if (!contains(e)) {
        return false;
      }
    }
    return true;
  }

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

  Sequence reverse();

  @Override
   Sequence map(Function1 mapper);

  @Override
   Sequence flatMap(Function1, ? extends R>> mapper);

  Sequence filter(Matcher1 matcher);

  Sequence filterNot(Matcher1 matcher);

  default Collection toCollection() {
    return toSequencedCollection();
  }

  default SequencedCollection toSequencedCollection() {
    return new SequenceCollection<>(this);
  }

  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 String join() {
    return join("");
  }

  default String join(String separator) {
    return stream().map(Object::toString).collect(joining(separator));
  }

  default String join(String separator, String prefix, String suffix) {
    return stream().map(Object::toString).collect(joining(separator, prefix, suffix));
  }

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

  @SuppressWarnings("unchecked")
  default  ImmutableMap> groupBy(Function1 selector) {
    return (ImmutableMap>)
        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 ImmutableTree asTree(Comparator comparator) {
    return ImmutableTree.from(comparator, stream());
  }

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

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

  default Stream> zipWithIndex() {
    return zip(Range.of(0, size()).stream(), stream());
  }

  default E[] toArray(Function1 supplier) {
    E[] array = supplier.apply(size());
    int i = 0;
    for (E element: this) {
      array[i++] = element;
    }
    return array;
  }

  static  ImmutableArray emptyArray() {
    return ImmutableArray.empty();
  }

  static  ImmutableList emptyList() {
    return ImmutableList.empty();
  }

  static  ImmutableSet emptySet() {
    return ImmutableSet.empty();
  }

  static  ImmutableTree emptyTree() {
    return ImmutableTree.empty();
  }

  @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 interleave(Iterator first, Iterator second) {
    return zip(first, second)
        .flatMap(tuple -> Stream.of(tuple.get1(), tuple.get2()))
        .filter(Objects::nonNull);
  }

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

  static  Stream interleave(Sequence first, Sequence second) {
    return interleave(first.stream(), second.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 = checkNonNull(first);
    this.second = checkNonNull(second);
  }

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

  @Override
  public Tuple2 next() {
    if (!hasNext()) {
      throw new NoSuchElementException();
    }
    return Tuple.of(nextItem(first), nextItem(second));
  }

  private static  Z nextItem(Iterator it) {
    return it.next();
  }
}

final class SequenceCollection implements SequencedCollection {

  private final Sequence sequence;

  SequenceCollection(Sequence sequence) {
    this.sequence = checkNonNull(sequence);
  }

  @Override
  public int size() {
    return sequence.size();
  }

  @Override
  public boolean isEmpty() {
    return sequence.isEmpty();
  }

  @Override
  public boolean contains(Object o) {
    return sequence.contains(o);
  }

  @Override
  public Iterator iterator() {
    return sequence.iterator();
  }

  @Override
  public Object[] toArray() {
    Object[] array = new Object[sequence.size()];
    int i = 0;
    for (E element: sequence) {
      array[i++] = element;
    }
    return array;
  }

  @SuppressWarnings("unchecked")
  @Override
  public  T[] toArray(T[] array) {
    if (array.length < sequence.size()) {
      return (T[]) Arrays.copyOf(toArray(), sequence.size(), array.getClass());
    }
    System.arraycopy(toArray(), 0, array, 0, sequence.size());
    if (array.length > sequence.size()) {
      array[sequence.size()] = null;
    }
    return array;
  }

  @Override
  public SequencedCollection reversed() {
    return new SequenceCollection<>(sequence.reverse());
  }

  @Override
  public boolean containsAll(Collection c) {
    return sequence.containsAll(c);
  }

  @Override
  public boolean add(E e) {
    throw new UnsupportedOperationException();
  }

  @Override
  public void addFirst(E e) {
    throw new UnsupportedOperationException();
  }

  @Override
  public void addLast(E e) {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean remove(Object o) {
    throw new UnsupportedOperationException();
  }

  @Override
  public E removeFirst() {
    throw new UnsupportedOperationException();
  }

  @Override
  public E removeLast() {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean removeIf(Predicate filter) {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean addAll(Collection c) {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean removeAll(Collection c) {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean retainAll(Collection c) {
    throw new UnsupportedOperationException();
  }

  @Override
  public void clear() {
    throw new UnsupportedOperationException();
  }
}