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

manifold.collections.extensions.java.lang.Iterable.ManIterableExt Maven / Gradle / Ivy

There is a newer version: 2024.1.33
Show newest version
/*
 * Copyright (c) 2018 - Manifold Systems LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package manifold.collections.extensions.java.lang.Iterable;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;

import manifold.ext.rt.api.*;
import manifold.rt.api.util.Pair;


import static java.util.Collections.emptyList;

/**
 */
@Extension
public class ManIterableExt
{
  /**
   * Returns first element.
   *
   * @throws NoSuchElementException if the collection is empty.
   */
  public static  T first( @This Iterable thiz )
  {
    if( thiz instanceof List )
    {
      return ((List)thiz).first();
    }
    else
    {
      Iterator iterator = thiz.iterator();
      if( !iterator.hasNext() )
      {
        throw new NoSuchElementException( "Collection is empty." );
      }
      return iterator.next();
    }
  }

  /**
   * Returns the first element matching the given {@code predicate}.
   *
   * @throws {@code NoSuchElementException} if no such element is found.
   */
  public static  T first( @This Iterable thiz, Predicate predicate )
  {
    for( T element : thiz )
    {
      if( predicate.test( element ) )
      {
        return element;
      }
    }
    throw new NoSuchElementException( "Collection contains no element matching the predicate." );
  }

  /**
   * Returns index of the first element matching the given {@code predicate}, or -1 if the collection does not contain such element.
   */
  public static  int indexOfFirst( @This Iterable thiz, Predicate predicate )
  {
    int index = 0;
    for( T item : thiz )
    {
      if( predicate.test( item ) )
      {
        return index;
      }
      index++;
    }
    return -1;
  }

  /**
   * Returns the first element, or null if the collection is empty.
   */
  public static  T firstOrNull( @This Iterable thiz )
  {
    if( thiz instanceof List )
    {
      if( ((List)thiz).isEmpty() )
      {
        return null;
      }
      else
      {
        return ((List)thiz).get( 0 );
      }
    }
    else
    {
      Iterator iterator = thiz.iterator();
      if( !iterator.hasNext() )
      {
        return null;
      }
      return iterator.next();
    }
  }

  /**
   * Returns the first element matching the given {@code predicate}, or {@code null} if element was not found.
   */
  public static  T firstOrNull( @This Iterable thiz, Predicate predicate )
  {
    for( T element : thiz )
    {
      if( predicate.test( element ) )
      {
        return element;
      }
    }
    return null;
  }

  /**
   * Returns the last element.
   *
   * @throws NoSuchElementException if the collection is empty.
   */
  public static  T last( @This Iterable thiz )
  {
    if( thiz instanceof List )
    {
      return ((List)thiz).last();
    }
    else
    {
      Iterator iterator = thiz.iterator();
      if( !iterator.hasNext() )
      {
        throw new NoSuchElementException( "Collection is empty." );
      }
      T last = iterator.next();
      while( iterator.hasNext() )
      {
        last = iterator.next();
      }
      return last;
    }
  }

  /**
   * Returns the last element matching the given {@code predicate}.
   *
   * @throws NoSuchElementException if no such element is found.
   */
  public static  T last( @This Iterable thiz, Predicate predicate )
  {
    T last = null;
    boolean found = false;
    for( T element : thiz )
    {
      if( predicate.test( element ) )
      {
        last = element;
        found = true;
      }
    }
    if( !found )
    {
      throw new NoSuchElementException( "Collection contains no element matching the predicate." );
    }
    return last;
  }

  /**
   * Returns index of the last element matching the given {@code predicate}, or -1 if the collection does not contain such element.
   */
  public static  int indexOfLast( @This Iterable thiz, Predicate predicate )
  {
    int lastIndex = -1;
    int index = 0;
    for( T item : thiz )
    {
      if( predicate.test( item ) )
      {
        lastIndex = index;
      }
      index++;
    }
    return lastIndex;
  }

  /**
   * Returns the last element, or {@code null} if the collection is empty.
   */
  public static  T lastOrNull( @This Iterable thiz )
  {
    if( thiz instanceof List )
    {
      return ((List)thiz).isEmpty() ? null : ((List)thiz).get( ((List)thiz).size() - 1 );
    }
    else
    {
      Iterator iterator = thiz.iterator();
      if( !iterator.hasNext() )
      {
        return null;
      }
      T last = iterator.next();
      while( iterator.hasNext() )
      {
        last = iterator.next();
      }
      return last;
    }
  }

  /**
   * Returns the last element matching the given {@code predicate}, or {@code null} if no such element was found.
   */
  public static  T lastOrNull( @This Iterable thiz, Predicate predicate )
  {
    T last = null;
    for( T element : thiz )
    {
      if( predicate.test( element ) )
      {
        last = element;
      }
    }
    return last;
  }

  /**
   * Returns the single element, or throws an exception if the collection is empty or has more than one element.
   */
  public static  T single( @This Iterable thiz )
  {
    if( thiz instanceof List )
    {
      return ((List)thiz).single();
    }
    else
    {
      Iterator iterator = thiz.iterator();
      if( !iterator.hasNext() )
      {
        throw new NoSuchElementException( "Collection is empty." );
      }
      T single = iterator.next();
      if( iterator.hasNext() )
      {
        throw new IllegalArgumentException( "Collection has more than one element." );
      }
      return single;
    }
  }

  /**
   * Returns the single element matching the given {@code predicate}, or throws exception if there is no or more than one matching element.
   */
  public static  T single( @This Iterable thiz, Predicate predicate )
  {
    T single = null;
    boolean found = false;
    for( T element : thiz )
    {
      if( predicate.test( element ) )
      {
        if( found )
        {
          throw new IllegalArgumentException( "Collection contains more than one matching element." );
        }
        single = element;
        found = true;
      }
    }
    if( !found )
    {
      throw new NoSuchElementException( "Collection contains no element matching the predicate." );
    }
    return single;
  }

  /**
   * Returns single element, or {@code null} if the collection is empty or has more than one element.
   */
  public static  T singleOrNull( @This Iterable thiz )
  {
    if( thiz instanceof List )
    {
      return ((List)thiz).size() == 1 ? ((List)thiz).get( 0 ) : null;
    }
    else
    {
      Iterator iterator = thiz.iterator();
      if( !iterator.hasNext() )
      {
        return null;
      }
      T single = iterator.next();
      if( iterator.hasNext() )
      {
        return null;
      }
      return single;
    }
  }

  /**
   * Returns the single element matching the given {@code predicate}, or {@code null} if element was not found or more than one element was found.
   */
  public static  T singleOrNull( @This Iterable thiz, Predicate predicate )
  {
    T single = null;
    boolean found = false;
    for( T element : thiz )
    {
      if( predicate.test( element ) )
      {
        if( found )
        {
          return null;
        }
        single = element;
        found = true;
      }
    }
    if( !found )
    {
      return null;
    }
    return single;
  }

  /**
   * Returns a list containing all elements matching the given {@code predicate}.
   */
  public static  List filterToList( @This Iterable thiz, Predicate predicate )
  {
    return thiz.filterTo( new ArrayList<>(), predicate );
  }

  /**
   * Appends all elements matching the given {@code predicate} to the given {@code destination}.
   */
  public static > C filterTo( @This Iterable thiz, C destination, Predicate predicate )
  {
    for( T element : thiz )
    {
      if( predicate.test( element ) )
      {
        destination.add( element );
      }
    }
    return destination;
  }

  /**
   * Returns a list containing only elements matching the given {@code predicate}.
   *
   * @param predicate function that takes the index of an element and the element itself
   *                  and returns the result of predicate evaluation on the element.
   */
  public static  List filterIndexedToList( @This Iterable thiz, IndexedPredicate predicate )
  {
    return thiz.filterIndexedTo( new ArrayList(), predicate );
  }

  /**
   * Appends all elements matching the given {@code predicate} to the given {@code destination}.
   *
   * @param predicate function that takes the index of an element and the element itself
   *                  and returns the result of predicate evaluation on the element.
   */
  public static > C filterIndexedTo( @This Iterable thiz, C destination, IndexedPredicate predicate )
  {
    thiz.forEachIndexed( ( index, element ) ->
                         {
                           if( predicate.test( index, element ) )
                           {
                             destination.add( element );
                           }
                         } );
    return destination;
  }

  /**
   * Returns a list containing all elements not matching the given {@code predicate}.
   */
  public static  List filterNotToList( @This Iterable thiz, Predicate predicate )
  {
    return thiz.filterNotTo( new ArrayList(), predicate );
  }

  /**
   * Appends all elements not matching the given {@code predicate} to the given {@code destination}.
   */
  public static > C filterNotTo( @This Iterable thiz, C destination, Predicate predicate )
  {
    for( T element : thiz )
    {
      if( !predicate.test( element ) )
      {
        destination.add( element );
      }
    }
    return destination;
  }

  /**
   * Returns a collection with elements in reversed order.
   * 

* Note, this method expires with JDK 21 because it interferes with new methods: {@code SequencedCollection#reversed()} * and {@code List#reversed()}.These new methods provide the same functionality, but only for SequencedCollection. */ @Expires(21) public static Collection reversed( @This Iterable thiz ) { List list = thiz.toList(); if( list.size() <= 1 ) { return list; } list.reverse(); return list; } /** * Returns a {@code List} containing all elements. */ public static List toList( @This Iterable thiz ) { if( thiz instanceof Collection ) { return ((Collection)thiz).toList(); } ArrayList list = new ArrayList<>(); for( T elem : thiz ) { list.add( elem ); } return list; } /** * Returns a {@code Set} containing all unique elements. *

* The returned set preserves the element iteration order of the original collection. */ public static Set toSet( @This Iterable thiz ) { if( thiz instanceof Collection ) { return ((Collection)thiz).toSet(); } LinkedHashSet set = new LinkedHashSet<>(); for( T elem : thiz ) { set.add( elem ); } return set; } /** * Returns a single list of all elements yielded from results of {@code transform} function being invoked on each element of original collection. */ public static List flatMap( @This Iterable thiz, Function> transform ) { return thiz.flatMapTo( new ArrayList(), transform ); } /** * Appends all elements yielded from results of {@code transform} function being invoked on each element of original collection, to the given {@code destination}. */ public static > C flatMapTo( @This Iterable thiz, C destination, Function> transform ) { for( T element : thiz ) { Iterable list = transform.apply( element ); destination.addAll( list ); } return destination; } /** * Returns a list containing only distinct elements from the given collection. *

* The elements in the resulting list are in the same order as they were in the source collection. */ public static List distinctList( @This Iterable thiz ) { return thiz.toSet().toList(); } /** * Returns a list containing only elements from the given collection * having distinct keys returned by the given {@code selector} function. *

* The elements in the resulting list are in the same order as they were in the source collection. */ public static List distinctBy( @This Iterable thiz, Function selector ) { HashSet set = new HashSet<>(); ArrayList list = new ArrayList<>(); for( T e : thiz ) { K key = selector.apply( e ); if( set.add( key ) ) { list.add( e ); } } return list; } /** * Returns a set containing all elements that are contained by both thiz set and the specified collection. *

* The returned set preserves the element iteration order of the original collection. */ public static Set intersect( @This Iterable thiz, Iterable other ) { Set set = thiz.toSet(); set.retainAll( coerceToUniqueCollection( other ) ); return set; } /** * Returns a set containing all elements that are contained by thiz collection and not contained by the specified collection. *

* The returned set preserves the element iteration order of the original collection. */ public static Set subtract( @This Iterable thiz, Iterable other ) { Set set = thiz.toSet(); set.removeAll( coerceToUniqueCollection( other ) ); return set; } /** * Returns a set containing all distinct elements from both collections. *

* The returned set preserves the element iteration order of the original collection. * Those elements of the {@code other} collection that are unique are iterated in the end * in the order of the {@code other} collection. */ public static Set union( @This Iterable thiz, Iterable other ) { Set set = thiz.toSet(); set.addAll( other ); return set; } private static Collection coerceToUniqueCollection( Iterable source ) { if( source instanceof Collection && ((Collection)source).size() <= 1 ) { return (Collection)source; } HashSet set = new HashSet<>(); for( T elem : source ) { set.add( elem ); } return set; } /** * Returns the number of elements in thiz collection. */ public static int count( @This Iterable thiz ) { if( thiz instanceof Collection ) { return ((Collection)thiz).size(); } int count = 0; for( T element : thiz ) { count++; } return count; } /** * Returns the number of elements matching the given {@code predicate}. */ public static int count( @This Iterable thiz, Predicate predicate ) { int count = 0; for( T element : thiz ) { if( predicate.test( element ) ) { count++; } } return count; } /** * Returns the first element having the largest value according to the provided {@code comparator} or {@code null} if there are no elements. */ public static T maxWith( @This Iterable thiz, Comparator comparator ) { Iterator iterator = thiz.iterator(); if( !iterator.hasNext() ) { return null; } T max = iterator.next(); while( iterator.hasNext() ) { T e = iterator.next(); if( comparator.compare( max, e ) < 0 ) { max = e; } } return max; } /** * Returns the first element having the smallest value according to the provided {@code comparator} or {@code null} if there are no elements. */ public static T minWith( @This Iterable thiz, Comparator comparator ) { Iterator iterator = thiz.iterator(); if( !iterator.hasNext() ) { return null; } T min = iterator.next(); while( iterator.hasNext() ) { T e = iterator.next(); if( comparator.compare( min, e ) > 0 ) { min = e; } } return min; } /** * Splits the original collection into pair of lists, * where first list contains elements for which {@code predicate} yielded {@code true}, * while second list contains elements for which {@code predicate} yielded {@code false}. */ public static Pair, List> partition( @This Iterable thiz, Predicate predicate ) { ArrayList first = new ArrayList(); ArrayList second = new ArrayList(); for( T element : thiz ) { if( predicate.test( element ) ) { first.add( element ); } else { second.add( element ); } } return new Pair<>( first, second ); } private static int collectionSizeOrDefault( Iterable thiz, int def ) { return thiz instanceof Collection ? ((Collection)thiz).size() : def; } /** * Returns a list containing the results of applying the given {@code transform} function * to each element in the original collection. */ public static List mapToList( @This Iterable thiz, Function transform ) { return thiz.mapTo( new ArrayList( collectionSizeOrDefault( thiz, 10 ) ), transform ); } /** * Returns a list containing the results of applying the given {@code transform} function * to each element and its index in the original collection. * * @param transform function that takes the index of an element and the element itself * and returns the result of the transform applied to the element. * @deprecated Use {@link #mapIndexedToList(Iterable, IndexedFunction)} instead. */ @Deprecated public static List mapIndexed( @This Iterable thiz, IndexedFunction transform ) { return thiz.mapIndexedTo( new ArrayList( collectionSizeOrDefault( thiz, 10 ) ), transform ); } /** * Returns a list containing the results of applying the given {@code transform} function * to each element and its index in the original collection. * * @param transform function that takes the index of an element and the element itself * and returns the result of the transform applied to the element. */ public static List mapIndexedToList( @This Iterable thiz, IndexedFunction transform ) { return thiz.mapIndexedTo( new ArrayList( collectionSizeOrDefault( thiz, 10 ) ), transform ); } /** * Returns a list containing only the non-null results of applying the given {@code transform} function * to each element and its index in the original collection. * * @param transform function that takes the index of an element and the element itself * and returns the result of the transform applied to the element. * @deprecated Use {@link #mapIndexedNotNullToList(Iterable, IndexedFunction)} instead. */ @Deprecated public static List mapIndexedNotNull( @This Iterable thiz, IndexedFunction transform ) { return thiz.mapIndexedNotNullTo( new ArrayList(), transform ); } /** * Returns a list containing only the non-null results of applying the given {@code transform} function * to each element and its index in the original collection. * * @param transform function that takes the index of an element and the element itself * and returns the result of the transform applied to the element. */ public static List mapIndexedNotNullToList( @This Iterable thiz, IndexedFunction transform ) { return thiz.mapIndexedNotNullTo( new ArrayList(), transform ); } /** * Applies the given {@code transform} function to each element and its index in the original collection * and appends only the non-null results to the given {@code destination}. * * @param transform function that takes the index of an element and the element itself * and returns the result of the transform applied to the element. */ public static > C mapIndexedNotNullTo( @This Iterable thiz, C destination, IndexedFunction transform ) { thiz.forEachIndexed( ( index, element ) -> { R result = transform.apply( index, element ); if( result != null ) { destination.add( result ); } } ); return destination; } /** * Applies the given {@code transform} function to each element and its index in the original collection * and appends the results to the given {@code destination}. * * @param transform function that takes the index of an element and the element itself * and returns the result of the transform applied to the element. */ public static > C mapIndexedTo( @This Iterable thiz, C destination, IndexedFunction transform ) { int index = 0; for( T item : thiz ) { destination.add( transform.apply( index++, item ) ); } return destination; } /** * Returns a list containing only the non-null results of applying the given {@code transform} function * to each element in the original collection. * @deprecated Use {@link #mapNotNullToList(Iterable, Function)} instead. */ @Deprecated public static List mapNotNull( @This Iterable thiz, Function transform ) { return thiz.mapNotNullTo( new ArrayList<>(), transform ); } /** * Returns a list containing only the non-null results of applying the given {@code transform} function * to each element in the original collection. */ public static List mapNotNullToList( @This Iterable thiz, Function transform ) { return thiz.mapNotNullTo( new ArrayList<>(), transform ); } /** * Applies the given {@code transform} function to each element in the original collection * and appends only the non-null results to the given {@code destination}. */ public static > C mapNotNullTo( @This Iterable thiz, C destination, Function transform ) { thiz.forEach( element -> { R result = transform.apply( element ); if( result != null ) { destination.add( result ); } } ); return destination; } /** * Applies the given {@code transform} function to each element of the original collection * and appends the results to the given {@code destination}. */ public static > C mapTo( @This Iterable thiz, C destination, Function transform ) { for( T item : thiz ) { destination.add( transform.apply( item ) ); } return destination; } /** * Returns a list containing all the elmeents from {@code fromIndex} (inclusive) */ public static List subList( @This Iterable thiz, int fromIndex ) { return thiz.subList( fromIndex, -1 ); } /** * Returns a list containing the elmeents {@code fromIndex} (inclusive) to {@code toIndex} (exclusive) */ public static List subList( @This Iterable thiz, int fromIndex, int toIndex ) { if( thiz instanceof Collection && ((Collection)thiz).isEmpty() ) { return emptyList(); } boolean toEnd = toIndex < 0; if( thiz instanceof List ) { //noinspection unchecked return ((List)thiz).subList( fromIndex, !toEnd ? toIndex : ((List)thiz).size() ); } ArrayList list = new ArrayList<>(); Iterator iter = thiz.iterator(); for( int i = 0; (toEnd || i < toIndex) && iter.hasNext(); i++ ) { T elem = iter.next(); if( i >= fromIndex ) { list.add( elem ); } } return list.optimizeReadOnlyList(); } /** * Performs the given {@code action} on each element, providing sequential index with the element. * * @param action function that takes the index of an element and the element itself * and performs the desired action on the element. */ public static void forEachIndexed( @This Iterable thiz, IndexedConsumer action ) { int index = 0; for( T item : thiz ) { action.accept( index++, item ); } } /** * Join the elements together in a String separated by {@code separator}. */ public static String joinToString( @This Iterable thiz, CharSequence separator ) { return thiz.joinTo( new StringBuilder(), separator ).toString(); } /** * Append the elements to {@code buffer} separated by {@code separator}. */ public static A joinTo( @This Iterable thiz, A buffer, CharSequence separator ) { int count = 0; try { for( T e : thiz ) { if( count++ > 0 ) { buffer.append( separator ); } buffer.append( e.toString() ); } } catch( Exception e ) { throw new RuntimeException( e ); } return buffer; } /** * Accumulates value starting with {@code demo} value and applying {@code operation} from left to right to current accumulator value and each element. *

* The operation is terminal. */ public static R fold( @This Iterable thiz, R initial, BiFunction operation ) { R accumulator = initial; for( T element : thiz ) { accumulator = operation.apply( accumulator, element ); } return accumulator; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy