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

com.jnape.palatable.lambda.lens.lenses.MapLens Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version
package com.jnape.palatable.lambda.lens.lenses;

import com.jnape.palatable.lambda.adt.Maybe;
import com.jnape.palatable.lambda.adt.hlist.Tuple2;
import com.jnape.palatable.lambda.functions.builtin.fn2.Filter;
import com.jnape.palatable.lambda.lens.Lens;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;

import static com.jnape.palatable.lambda.adt.Maybe.maybe;
import static com.jnape.palatable.lambda.functions.builtin.fn2.Eq.eq;
import static com.jnape.palatable.lambda.functions.builtin.fn2.Map.map;
import static com.jnape.palatable.lambda.functions.builtin.fn2.ToCollection.toCollection;
import static com.jnape.palatable.lambda.functions.builtin.fn2.ToMap.toMap;
import static com.jnape.palatable.lambda.lens.Lens.simpleLens;
import static com.jnape.palatable.lambda.lens.functions.View.view;
import static com.jnape.palatable.lambda.lens.lenses.MaybeLens.unLiftA;
import static com.jnape.palatable.lambda.lens.lenses.MaybeLens.unLiftB;

/**
 * Lenses that operate on {@link Map}s.
 */
public final class MapLens {

    private MapLens() {
    }

    /**
     * A lens that focuses on a copy of a Map. Useful for composition to avoid mutating a map reference.
     *
     * @param  the key type
     * @param  the value type
     * @return a lens that focuses on copies of maps
     */
    public static  Lens.Simple, Map> asCopy() {
        return simpleLens(HashMap::new, (__, copy) -> copy);
    }

    /**
     * A lens that focuses on a value at a key in a map, as a {@link Maybe}.
     *
     * @param  the key type
     * @param  the value type
     * @param k   the key to focus on
     * @return a lens that focuses on the value at key, as a {@link Maybe}
     */
    public static  Lens.Simple, Maybe> valueAt(K k) {
        return simpleLens(m -> maybe(m.get(k)), (m, maybeV) -> {
            Map updated = new HashMap<>(m);
            return maybeV.fmap(v -> {
                updated.put(k, v);
                return updated;
            }).orElseGet(() -> {
                updated.remove(k);
                return updated;
            });
        });
    }

    /**
     * A lens that focuses on a value at a key in a map, falling back to defaultV if the value is missing.
     * 

* Note that this lens is NOT lawful, since "putting back what you got changes nothing" fails for any value * B where S is the empty map * * @param k the key to focus on * @param defaultValue the default value to use in case of a missing value at key * @param the key type * @param the value type * @return a lens that focuses on the value at the key */ @SuppressWarnings("unchecked") public static Lens.Simple, V> valueAt(K k, V defaultValue) { return unLiftB(unLiftA(valueAt(k), defaultValue))::apply; } /** * A lens that focuses on the keys of a map. * * @param the key type * @param the value type * @return a lens that focuses on the keys of a map */ public static Lens.Simple, Set> keys() { return simpleLens(m -> new HashSet<>(m.keySet()), (m, ks) -> { HashSet ksCopy = new HashSet<>(ks); Map updated = new HashMap<>(m); Set keys = updated.keySet(); keys.retainAll(ksCopy); ksCopy.removeAll(keys); ksCopy.forEach(k -> updated.put(k, null)); return updated; }); } /** * A lens that focuses on the values of a map. In the case of updating the map, only the entries with a value listed * in the update collection of values are kept. *

* Note that this lens is NOT lawful, since "you get back what you put in" fails for all values B that * represent a non-surjective superset of the existing values in S. * * @param the key type * @param the value type * @return a lens that focuses on the values of a map */ public static Lens.Simple, Collection> values() { return simpleLens(m -> new ArrayList<>(m.values()), (m, vs) -> { Map updated = new HashMap<>(m); Set valueSet = new HashSet<>(vs); Set matchingKeys = Filter.>filter(kv -> valueSet.contains(kv.getValue())) .andThen(map(Map.Entry::getKey)) .andThen(toCollection(HashSet::new)) .apply(updated.entrySet()); updated.keySet().retainAll(matchingKeys); return updated; }); } /** * A lens that focuses on the inverse of a map (keys and values swapped). In the case of multiple equal values * becoming keys, the last one wins. * * @param the key type * @param the value type * @return a lens that focuses on the inverse of a map */ public static Lens.Simple, Map> inverted() { return simpleLens(m -> { Map inverted = new HashMap<>(); m.forEach((key, value) -> inverted.put(value, key)); return inverted; }, (m, im) -> { Map updated = new HashMap<>(m); updated.clear(); updated.putAll(view(inverted(), im)); return updated; }); } /** * A lens that focuses on a map while mapping its values with the mapping function. *

* Note that this lens is NOT lawful, since "you get back what you put in" fails for all values B that * do not map from the current values in S (new mappings cannot be preserved as the inversion of * fn is not known). * * @param fn the mapping function * @param the key type * @param the unfocused map value type * @param the focused map value types * @return a lens that focuses on a map while mapping its values */ public static Lens.Simple, Map> mappingValues(Function fn) { return simpleLens(m -> toMap(HashMap::new, map(t -> t.biMapR(fn), map(Tuple2::fromEntry, m.entrySet()))), (s, b) -> { //todo: remove this madness upon arrival of either invertible functions or Iso Set retainKeys = Filter.>filter(kv -> eq(fn.apply(kv.getValue()), b.get(kv.getKey()))) .andThen(map(Map.Entry::getKey)) .andThen(toCollection(HashSet::new)) .apply(s.entrySet()); Map copy = new HashMap<>(s); copy.keySet().retainAll(retainKeys); return copy; }); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy