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

edu.isi.nlp.collections.MapUtils Maven / Gradle / Ivy

The newest version!
package edu.isi.nlp.collections;

import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Ordering;
import edu.isi.nlp.StringUtils;
import edu.isi.nlp.collections.IterableUtils.ZipPair;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * Utilities for working with {@link Map}s
 *
 * @author Ryan Gabbard, Jay DeYoung
 */
public final class MapUtils {

  private MapUtils() {
    throw new UnsupportedOperationException();
  }

  /** Pairs up the values of a map by their common keys. */
  public static  PairedMapValues zipValues(final Map left, final Map right) {
    checkNotNull(left);
    checkNotNull(right);
    final ImmutableList.Builder> pairedValues = ImmutableList.builder();
    final ImmutableList.Builder leftOnly = ImmutableList.builder();
    final ImmutableList.Builder rightOnly = ImmutableList.builder();

    for (final Map.Entry leftEntry : left.entrySet()) {
      final K key = leftEntry.getKey();
      if (right.containsKey(key)) {
        pairedValues.add(ZipPair.from(leftEntry.getValue(), right.get(key)));
      } else {
        leftOnly.add(leftEntry.getValue());
      }
    }

    for (final Map.Entry rightEntry : right.entrySet()) {
      if (!left.containsKey(rightEntry.getKey())) {
        rightOnly.add(rightEntry.getValue());
      }
    }

    return new PairedMapValues(pairedValues.build(), leftOnly.build(), rightOnly.build());
  }

  /**
   * Return a copy of the input map with keys transformed by {@code keyInjection} and values
   * transformed by {@code valueFunction}. Beware: {@code keyInjection} must be an injection over
   * all the keys of the input map. If two original keys are mapped to the same value, an {@link
   * java.lang.IllegalArgumentException} will be thrown.
   *
   * 

Neither {@code keyInjection} nor {@code valueFunction} may return null. If one does, an * exception will be thrown. */ public static ImmutableMap copyWithTransformedEntries( Map input, Function keyInjection, Function valueFunction) { final ImmutableMap.Builder ret = ImmutableMap.builder(); for (final Map.Entry entry : input.entrySet()) { ret.put(keyInjection.apply(entry.getKey()), valueFunction.apply(entry.getValue())); } return ret.build(); } public static class PairedMapValues { public PairedMapValues( final List> pairedValues, final List leftOnly, final List rightOnly) { this.pairedValues = ImmutableList.copyOf(pairedValues); this.leftOnly = ImmutableList.copyOf(leftOnly); this.rightOnly = ImmutableList.copyOf(rightOnly); } public List> pairedValues() { return pairedValues; } public List leftOnly() { return leftOnly; } public List rightOnly() { return rightOnly; } public boolean perfectlyAligned() { return leftOnly.isEmpty() && rightOnly.isEmpty(); } private final List> pairedValues; private final List leftOnly; private final List rightOnly; } /** Builds a map from sequence items to their zero-indexed positions in the sequence. */ public static ImmutableMap indexMap(Iterable items) { final ImmutableMap.Builder ret = ImmutableMap.builder(); int idx = 0; for (final T item : items) { ret.put(item, idx++); } return ret.build(); } /** Gets the union of the {@code keySet()}s of all provided {@link Maps}s. */ public static ImmutableSet allKeys(final Iterable> maps) { final ImmutableSet.Builder builder = ImmutableSet.builder(); for (final Map map : maps) { builder.addAll(map.keySet()); } return builder.build(); } /** Gets the union of the {@code keySet()}s of all provided {@link Multimap}s. */ public static ImmutableSet allMultimapKeys( final Iterable> multimaps) { final ImmutableSet.Builder builder = ImmutableSet.builder(); for (final Multimap multimap : multimaps) { builder.addAll(multimap.keySet()); } return builder.build(); } public static Function, V> entryValueFunction() { return new Function, V>() { @Override public V apply(final Map.Entry entry) { return entry.getValue(); } }; } public static Function, K> entryKeyFunction() { return new Function, K>() { @Override public K apply(final Map.Entry entry) { return entry.getKey(); } }; } public static Function, Iterable> mapValuesFunction() { return new Function, Iterable>() { @Override public Iterable apply(final Map input) { return input.values(); } }; } /** * A partial ordering over {@link Map.Entry} according to their values, in ascending natural * order. */ public static > Ordering> byValueOrderingAscending() { return Ordering.natural().onResultOf(Entry::getValue); } /** * A partial ordering over {@link Map.Entry} according to their values, in descending natural * order. */ public static > Ordering> byValueOrderingDescending() { return MapUtils.byValueOrderingAscending().reverse(); } /** * A partial ordering over {@link Map.Entry} according to {@code valueOrdering} applied to their * values. */ public static Ordering> byValueOrdering(final Ordering valueOrdering) { return valueOrdering.onResultOf(Entry::getValue); } /** A partial ordering of {@link Map.Entry} by the reverse of the natural ordering of the keys. */ public static , V> Ordering> byKeyDescendingOrdering() { return Ordering.natural().onResultOf(MapUtils.entryKeyFunction()); } /** A partial ordering of {@link Map.Entry} by the natural ordering of the keys. */ public static Ordering> byKeyOrdering(Ordering keyOrdering) { return keyOrdering.onResultOf(Entry::getKey); } /** * Creates a copy of the supplied map with its keys transformed by the supplied function, which * must be one-to-one on the keys of the original map. If two keys are mapped to the same value by * {@code injection}, an {@code IllegalArgumentException} is thrown. */ public static ImmutableMap copyWithKeysTransformedByInjection( final Map map, final Function injection) { final ImmutableMap.Builder ret = ImmutableMap.builder(); for (final Map.Entry entry : map.entrySet()) { ret.put(injection.apply(entry.getKey()), entry.getValue()); } return ret.build(); } /** * Returns the length of the longest key in a map, or 0 if the map is empty. Useful for printing * tables, etc. The map may not have any null keys. */ public static int longestKeyLength(Map map) { if (map.isEmpty()) { return 0; } return Ordering.natural() .max(FluentIterable.from(map.keySet()).transform(StringUtils.lengthFunction())); } /** * Just like {@link com.google.common.base.Functions#forMap(Map, Object)} except it allows a * potentially non-constant {@link Function} as the default. */ public static Function asFunction( Map map, Function defaultFunction) { return new ForMapWithDefaultFunction(map, defaultFunction); } // indebted to Guava's Functions.forMap() private static class ForMapWithDefaultFunction implements Function { private final Map map; private final Function defaultFunction; private ForMapWithDefaultFunction( final Map map, final Function defaultFunction) { this.map = checkNotNull(map); this.defaultFunction = checkNotNull(defaultFunction); } @Override public V apply(final K input) { final V result = map.get(input); return (result != null || map.containsKey(input)) ? result : defaultFunction.apply(input); } @Override public int hashCode() { return Objects.hashCode(map, defaultFunction); } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } final ForMapWithDefaultFunction other = (ForMapWithDefaultFunction) obj; return Objects.equal(this.map, other.map) && Objects.equal(this.defaultFunction, other.defaultFunction); } @Override public String toString() { return "asFunction(" + map + ", default=" + defaultFunction + ")"; } } /** * Creates a {@link ImmutableMap} by pairing up a sequence of keys and values. The {@code n}-th * key is paired to the {@code n}-th value. Neither null keys nor null values are permitted. The * keys must be unique or an {@link IllegalArgumentException} will be thrown. If the numberof * elements returned by the two {@link Iterable}s differ, an {@link IllegalArgumentException} will * be thrown. */ public static ImmutableMap copyParallelListsToMap( Iterable keys, Iterable values) { final ImmutableMap.Builder ret = ImmutableMap.builder(); final Iterator keyIt = keys.iterator(); final Iterator valueIt = values.iterator(); while (keyIt.hasNext() && valueIt.hasNext()) { ret.put(keyIt.next(), valueIt.next()); } if (!keyIt.hasNext() && !valueIt.hasNext()) { return ret.build(); } else { if (keyIt.hasNext()) { throw new IllegalArgumentException( "When pairing keys and values, there were more keys than values"); } else { throw new IllegalArgumentException( "When pairing keys and values, there were more values than keys"); } } } /** * Creates a {@code LaxImmutableMapBuilder} which will behave exactly like a {@link * com.google.common.collect.ImmutableMap.Builder} except it will allow adding the same key-value * pair more than once. "Same" is determined by the value's equality method. The behavior of this * builder and the resulting {@link ImmutableMap} is deterministic. */ public static LaxImmutableMapBuilder immutableMapBuilderAllowingSameEntryTwice() { return new MonotonicLaxImmutableMapBuilder<>(false); } /** * Creates a {@code LaxImmutableMapBuilder} which will behave exactly like a {@link * com.google.common.collect.ImmutableMap.Builder} except it will silently ignore any attempt to * add a new entry with a previously seen key. The behavior of this builder and the resulting * {@link ImmutableMap} is deterministic. */ public static LaxImmutableMapBuilder immutableMapBuilderIgnoringDuplicates() { return new MonotonicLaxImmutableMapBuilder<>(true); } /** * Creates a {@code LaxImmutableMapBuilder} which will behave exactly like a {@link * com.google.common.collect.ImmutableMap.Builder} except that when attempting to add a value for * a previously seen key, the greater of the older value and the new value according to the * provided comparator will be used. In case of a tie, the incumbent stays. * *

The behavior of this builder and the resulting {@link ImmutableMap} will be deterministic if * the supplied comparator is. */ public static LaxImmutableMapBuilder immutableMapBuilderResolvingDuplicatesBy( Comparator conflictComparator) { return new NonMonotonicLaxImmutableMapBuilder<>(Ordering.from(conflictComparator)); } /** * Returns a Guava {@link Function} to transform map entries to strings by taking the {@link * Object#toString()} of the key and the value and joining them with the supplied {@code * separator}. None of the key, value, or separator may be null. */ public static Function, String> toStringWithKeyValueSeparator( final String separator) { return new EntryJoinerFunction<>(separator); } private static class EntryJoinerFunction implements Function, String> { private final String separator; public EntryJoinerFunction(final String separator) { this.separator = checkNotNull(separator); } @Override public String apply(final Entry input) { return input.getKey() + separator + input.getValue(); } @Override public int hashCode() { return Objects.hashCode(separator); } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } final EntryJoinerFunction other = (EntryJoinerFunction) obj; return Objects.equal(this.separator, other.separator); } } /** * Just like {@link Maps#transformEntries(Map, Maps.EntryTransformer)} but on an {@link Iterable} * of {@link Map.Entry}. */ public static Iterable> transformValues( Iterable> input, final Function valueTransformer) { return Iterables.transform( input, new Function, Entry>() { @Override public Entry apply(final Entry input) { return Maps.immutableEntry(input.getKey(), valueTransformer.apply(input.getValue())); } }); } /** * Just like {@link Maps#transformEntries(Map, Maps.EntryTransformer)} but on an {@link Iterable} * of {@link Map.Entry}. */ public static Iterable> transformEntries( Iterable> input, final Maps.EntryTransformer entryTransformer) { return Iterables.transform( input, new Function, Entry>() { @Override public Entry apply(final Entry input) { return Maps.immutableEntry( input.getKey(), entryTransformer.transformEntry(input.getKey(), input.getValue())); } }); } /** * Creates a {@link com.google.common.collect.Maps.EntryTransformer} which applies the supplied * {@link Function} to map entry values. */ public static Maps.EntryTransformer valueTransformer( final Function valueFunction) { checkNotNull(valueFunction); return new Maps.EntryTransformer() { @Override public V2 transformEntry(final K key, final V1 value) { return valueFunction.apply(value); } }; } /** * Returns a wrapper around an {@link com.google.common.collect.ImmutableMultimap.Builder} which * hides the differences between it and {@link com.google.common.collect.ImmutableMap.Builder} for * the purpose of population. */ public static KeyValueSink asMapSink(ImmutableMultimap.Builder multimapB) { return new ImmutableMultimapBuilderSink<>(multimapB); } /** * Returns a wrapper around an {@link com.google.common.collect.ImmutableMap.Builder} which hides * the differences between it and {@link com.google.common.collect.ImmutableMultimap.Builder} for * the purpose of population. Beware {@link com.google.common.collect.ImmutableMap.Builder} will * still throw an exception for duplicate entries! */ public static KeyValueSink asMapSink(ImmutableMap.Builder mapB) { return new ImmutableMapBuilderSink<>(mapB); } } abstract class AbstractKeyValueSink implements KeyValueSink { @Override public final KeyValueSink put(final Entry entry) { put(entry.getKey(), entry.getValue()); return this; } @Override public final KeyValueSink putAll( final Iterable> entries) { for (final Entry entry : entries) { put(entry.getKey(), entry.getValue()); } return this; } } final class ImmutableMapBuilderSink extends AbstractKeyValueSink { final ImmutableMap.Builder builder; ImmutableMapBuilderSink(final ImmutableMap.Builder builder) { this.builder = checkNotNull(builder); } @Override public KeyValueSink put(final K key, final V value) { builder.put(key, value); return this; } } final class ImmutableMultimapBuilderSink extends AbstractKeyValueSink { final ImmutableMultimap.Builder builder; ImmutableMultimapBuilderSink(final ImmutableMultimap.Builder builder) { this.builder = checkNotNull(builder); } @Override public KeyValueSink put(final K key, final V value) { builder.put(key, value); return this; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy