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

com.medallia.word2vec.util.Pair Maven / Gradle / Ivy

There is a newer version: 0.10.3
Show newest version
package com.medallia.word2vec.util;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * Simple class for storing two arbitrary objects in one.
 *
 * @param  the type of the first value
 * @param  the type of the second value
 */
public class Pair implements Map.Entry, Serializable {
	
	/** @see Serializable */
	private static final long serialVersionUID = 1L;

	/** The first item in the pair. */
	public final K first;
	/** The second item in the pair. */
	public final V second;
	
	/** Creates a new instance of Pair */
	protected Pair(K first, V second) {
		this.first = first;
		this.second = second;
	}

	/** Type-inferring constructor */
	public static  Pair cons(X x, Y y) { return new Pair(x,y); }
	
	/** Type-inferring constructor for pairs of the same type, which can optionally be swapped */
	public static  Pair cons(X x, X y, boolean swapped) { return swapped ? new Pair(y, x) : new Pair(x, y); }

	@Override
	public int hashCode() {
		// Compute by hand instead of using Encoding.combineHashes for improved performance
		return (first == null ? 0 : first.hashCode() * 13) + (second == null ? 0 : second.hashCode() * 17);
	}
	
	@Override
	public boolean equals(Object o) {
		if (o == this)
			return true;
		if (o == null || !getClass().equals(o.getClass()))
			return false;
		
		Pair op = (Pair) o;
		return Objects.equals(op.first, first) && Objects.equals(op.second, second);
	}

	/** @return {@link #first}; needed because String Templates have 'first' as a reserved word. */
	public K getOne() { return first; }
	/** @return {@link #first} */
	public K getFirst() { return first; }
	/** @return {@link #second} */
	public V getSecond() { return second; }
	
	@Override public String toString() {
		return "Pair<"+first+","+second+">";
	}
	
	/** @return a list with the two elements from this pair, regardless of whether they are null or not */
	public List asList() {
		return Lists.newArrayList(first, second);
	}
	
	/**
	 * @return a list of key/value pairs (keys are at even indices, values at odd) taken from the
	 *         given array, whose length must be even.
	 */
	@SafeVarargs
	public static  List> fromPairs(X... args) {
		if (Common.isOdd(args.length))
			throw new IllegalArgumentException("Array length must be even: " + args.length);
		
		List> l = new ArrayList<>(args.length / 2);
		for (int i = 0; i < args.length; i += 2)
			l.add(Pair.cons(args[i], args[i + 1]));
		return l;
	}
	
	/**
	 * Converts a Map to a List of pairs.
	 * Each entry in the map results in a Pair in the returned list.
	 */
	public static  List> fromMap(Map m) {
		List> l = new ArrayList<>();
		for (Map.Entry me : m.entrySet()) {
			l.add(Pair.cons(me.getKey(), me.getValue()));
		}
		return l;
	}
	
	private static >> C fromMapFlatten(C c, Map> m) {
		for (Map.Entry> me : m.entrySet()) {
			for (Y y : me.getValue())
				c.add(Pair.cons(me.getKey(), y));
		}
		return c;
	}
	
	@Override public K getKey() { return first; }
	@Override public V getValue() { return second; }
	@Override public V setValue(V value) { throw new UnsupportedOperationException(); }
	
	/** Method that allows Pair to be used directly by the Setup system (wtf) */
	public String getName() { return String.valueOf(second); }

	/** @return a reversed version of this pair */
	public Pair swapped() { return Pair.cons(second, first); }

	/** @return {@link Function} which performs a {@link #swapped()} */
	public static  Function, Pair> swappedFunction() {
		return new Function, Pair>() {
			@Override public Pair apply(Pair p) {
				return p.swapped();
			}
		};
	}
	
	/**
	 * @return {@link Ordering} which compares the first value of the pairs.
	 * Pairs with equal first value will be considered equivalent independent of the second value
	 */
	public static > Ordering> firstComparator() {
		return new Ordering>() {
			@Override public int compare(Pair o1, Pair o2) {
				return Compare.compare(o1.first, o2.first);
			}
		};
	}
	
	/**
	 * @return {@link Ordering} which compares the second value of the pairs.
	 * Pairs with equal second value will be considered equivalent independent of the first value
	 */
	public static > Ordering> secondComparator() {
		return new Ordering>() {
			@Override public int compare(Pair o1, Pair o2) {
				return Compare.compare(o1.second, o2.second);
			}
		};
	}
	
	/** @return {@link Ordering} which compares both values of the {@link Pair}s, with the first taking precedence. */
	public static , Y extends Comparable> Ordering> firstThenSecondComparator() {
		return new Ordering>() {
			@Override public int compare(Pair o1, Pair o2) {
				int k = Compare.compare(o1.first, o2.first);
				if (k == 0) k = Compare.compare(o1.second, o2.second);
				return k;
			}
		};
	}
	
	/** @return {@link Ordering} which compares both values of the {@link Pair}s, with the second taking precedence. */
	public static , Y extends Comparable> Ordering> secondThenFirstComparator() {
		return new Ordering>() {
			@Override public int compare(Pair o1, Pair o2) {
				int k = Compare.compare(o1.second, o2.second);
				if (k == 0)
					k = Compare.compare(o1.first, o2.first);
				return k;
			}
		};
	}
	
	/**
	 * Pair comparator that applies the given {@link Comparator} to the first value of the pairs
	 */
	public static  Comparator> firstComparator(final Comparator comp) {
		return new Comparator>() {
			@Override public int compare(Pair o1, Pair o2) {
				return comp.compare(o1.first, o2.first);
			}
		};
	}

	/**
	 * Pair comparator that applies the given {@link Comparator} to the second value of the pairs
	 */
	public static  Comparator> secondComparator(final Comparator comp) {
		return new Comparator>() {
			@Override public int compare(Pair o1, Pair o2) {
				return comp.compare(o1.second, o2.second);
			}
		};
	}
	
	/**
	 * Pair comparator that compares both values of the pairs, with the first taking
	 * precedence; the order is reversed for the first value only.
	 */
	public static , Y extends Comparable> Comparator> bothFirstReversedComparator() {
		return new Comparator>() {
			@Override public int compare(Pair o1, Pair o2) {
				int k = Compare.compare(o2.first, o1.first);
				if (k == 0) k = Compare.compare(o1.second, o2.second);
				return k;
			}
		};
	}
	
	private static  Map fillMap(Map m, Iterable> pairs) {
		for (Pair p : pairs) {
			m.put(p.first, p.second);
		}
		return m;
	}

	/** @return the combination of all the elements in each collection. For instance if the first collection is
	 * {@code [1, 2, 3]}, and the second one is {@code [a, b]}, then the result is {@code [(1, a), (1, b), (2, a), ...]}
	 */
	@SuppressWarnings("unchecked")
	public static  List> cartesianProduct(Collection c1, Collection c2) {
		return FluentIterable.from(Sets.cartesianProduct(ImmutableSet.copyOf(c1), ImmutableSet.copyOf(c2)))
				.transform(new Function, Pair>() {
					@Override public Pair apply(List objs) {
						X x = (X) objs.get(0);
						Y y = (Y) objs.get(1);
						return Pair.cons(x, y);
					}
				})
				.toList();
	}
	
	/** @return the elements at equal indices in the two lists, which must be of the same
	 * length, as pairs.
	 */
	public static  List> zip(Collection c1, Collection c2) {
		return zip(c1, c2, new ArrayList>(c1.size()), false);
	}
	
	/** @return the elements at equal indices in the two lists, which must be of the same
	 * length, as pairs.
	 */
	public static  List> zip(X[] a1, Y[] a2) {
		return zip(ImmutableList.copyOf(a1), ImmutableList.copyOf(a2));
	}

	/**
	 * @return the elements at equal indices in the two lists, which must be of
	 *         the same length, as pairs, without duplicates removed from the
	 *         first list
	 */
	public static  List> zipUnique(Collection c1, Collection c2) {
		return zip(c1, c2, new ArrayList>(), true);
	}

	private static  List> zip(Collection c1, Collection c2, List> output, boolean uniqueKeys) {
		int size = c1.size();
		if (size != c2.size())
			throw new IllegalArgumentException("Collections must be of same size: " + size + ", " + c2.size());

		Set set = uniqueKeys ? new HashSet() : null;
		Iterator it1 = c1.iterator();
		Iterator it2 = c2.iterator();

		while (it1.hasNext() && it2.hasNext()) {
			X x = it1.next();
			Y y = it2.next();
			if (set == null || set.add(x))
				output.add(Pair.cons(x, y));
		}
		return output;
	}

	/** 
	 * @return the elements at equal indices of the two list as pairs. The number of elements in the result list
	 * is the minimum of the given iterable
	 * of different size only elements at indices
	 *  present on the first {@link Iterable} are used.
	 */
	public static  Iterable> zipInner(final Iterable first, final Iterable second) {
		return new Iterable>() {
			@Override public Iterator> iterator() {
				final Iterator x = first.iterator();
				final Iterator y = second.iterator();
				return new Iterator>() {
					@Override public boolean hasNext() {
						return x.hasNext() && y.hasNext();
					}

					@Override
					public Pair next() {
						return Pair.cons(x.next(), y.next());
					}

					@Override
					public void remove() {
						x.remove();
						y.remove();
					}
					
				};
			}
			
		};
	}

	/** @return {@link Function} which retrieves the second of the pair */
	public static  Function, V> retrieveSecondFunction() {
		return new Function, V>() {
			@Override
			public V apply(Pair p) {
				return p.second;
			}
		};
	}

	/** @return {@link Iterable} of second element in pair */
	public static  Iterable unzipSecond(Iterable> pairs) {
		return Iterables.transform(pairs, Pair.retrieveSecondFunction());
	}

	/** @return {@link Function} which maps the value of each pair through the given {@link Function} */
	public static  Function, Pair> mapValues(final Function func) {
		return new Function, Pair>() {
			@Override public Pair apply(Pair p) {
				return Pair.cons(p.first, func.apply(p.second));
			}
		};
	}

	/** @return the first value, or null if the pair is null */
	public static  K firstOrNull(Pair pair) {
		return pair != null ? pair.first : null;
	}
	
	/** @return the second value, or null if the pair is null */
	public static  V secondOrNull(Pair pair) {
		return pair != null ? pair.second : null;
	}
	
	/** @return {@link Predicates} which filters only on the first value */
	public static  Predicate> getFirstPredicate(final Predicate pred) {
		return new Predicate>() {
			@Override public boolean apply(Pair pair) {
				return pred.apply(pair.first);
			}
		};
	}
	
	/** @return {@link Predicates} which filters only on the second value */
	public static  Predicate> getSecondPredicate(final Predicate pred) {
		return new Predicate>() {
			@Override public boolean apply(Pair pair) {
				return pred.apply(pair.second);
			}
		};
	}
	
	/** @return {@link Predicates} which accepts a pair only if both values are accepted */
	public static  Predicate> getAndPredicate(final Predicate firstPred, final Predicate secondPred) {
		return new Predicate>() {
			@Override public boolean apply(Pair pair) {
				return firstPred.apply(pair.first) && secondPred.apply(pair.second);
			}
		};
	}
	
	/** @return {@link Predicates} which accepts a pair if either values is accepted */
	public static  Predicate> getOrPredicate(final Predicate firstPred, final Predicate secondPred) {
		return new Predicate>() {
			@Override public boolean apply(Pair pair) {
				return firstPred.apply(pair.first) || secondPred.apply(pair.second);
			}
		};
	}

	/**
	 * @return {@link ImmutableList} containing all values paired with their applied value
	 * through the function
	 */
	public static  ImmutableList> toPairList(Iterable values, Function func) {
		ImmutableList.Builder> result = ImmutableList.builder();
		for (X x : values)
			result.add(Pair.cons(x, func.apply(x)));
		return result.build();
	}

}