Category talk:Wren-maputil

From Rosetta Code

Source code

/* Module "maputil.wren" */

import "./check" for Check

/* MapUtil supplements the Map class with some other operations on maps. */
class MapUtil {
    // Creates a new Map from lists of keys and values
    // which must be of the same length.
    static create(keys, values) {
        Check.list("keys", keys)
        var count = keys.count
        Check.list("values", values, count, count)
        var m = {}
        for (i in 0...count) m[keys[i]] = values[i]
        return m
    }

    // Creates a new Map from a sequence of keys and assigns
    // the same value to all of them.
    static createSame(keys, value) {
        Check.seq("keys", keys)
        var m = {}
        for (key in keys) m[key] = value
        return m
    }

    // Adds entries to an existing Map from lists of keys and values
    // which must be of the same length.
    // Keys which are already present in the Map have their values replaced
    // if 'replace' is true but not otherwise.
    // Returns the Map after the additions.
    static addAll(map, keys, values, replace) {
        Check.map("map", map)
        Check.list("keys", keys)
        var count = keys.count
        Check.list("values", values, count, count)
        Check.bool("replace", replace)
        for (i in 0...count) {
            if (!replace && map.containsKey(key[i])) continue
            map[keys[i]] = values[i]
        }
        return map
    }

    // Adds entries to an existing Map from a sequence of keys and assigns
    // the same value to all of them.
    // Keys which are already present in the Map have their values replaced
    // if 'replace' is true but not otherwise.
    // Returns the Map after the additions.
    static addAllSame(map, keys, value, replace) {
        Check.map("map", map)
        Check.seq("keys", keys)
        Check.bool("replace", replace)
        for (key in keys) {
            if (!replace && map.containsKey(key)) continue
            map[key] = value
        }
        return map
    }

    // Merges all the elements of map2 into map1.
    // Keys which are already present in map1 have their values replaced
    // if 'replace' is true but not otherwise.
    // Returns map1 after the additions.
    // A specialized version of 'addAll'.
    static merge(map1, map2, replace) {
        Check.map("map1", map)
        Check.map("map2", map)
        for (key in map2.keys) {
            if (!replace && map1.containsKey(key)) continue
            map1[key] = map2[key]
        }
        return map
    }

    // Returns true if all keys in the 'keys' sequence are keys of an existing Map
    // or false otherwise.
    static containsAll(map, keys) {
        Check.map("map", map)
        Check.seq("keys", keys)
        for (key in keys) {
            if (!map[key]) return false
        }
        return true
    }

    // Returns true if any key in the 'keys' sequence is a key of an existing Map
    // or false otherwise.
    static containsAny(map, keys) {
        Check.map("map", map)
        Check.seq("keys", keys)
        for (key in keys) {
            if (map[key]) return true
        }
        return false
    }

    // Returns true if no key in the 'keys' sequence is a key of an existing Map
    // or false otherwise.
    static containsNone(map, keys) { !containsAny(map, keys) }

    // Removes all keys in the 'keys' sequence from an existing Map
    // and returns a list of the associated values removed.
    static removeAll(map, keys) {
        Check.map("map", map)
        if (map.isEmpty) return []
        Check.seq("keys", keys)
        var removals = []
        for (key in keys) {
            if (map.containsKey(key)) removals.add(map.remove(key))
        }
        return removals
    }

    // Removes all entries from 'map' whose key satisfies the predicate
    // function 'fn' and returns a list of the associated values removed.
    static removeBy(map, fn) {
        Check.map("map", map)
        Check.func("fn", fn, 1)
        if (map.isEmpty) return []
        var removals = []
        for (key in map.keys) {
            if (fn.call(key)) removals.add(map.remove(key))
        }
        return removals
    }

    // Copies the elements of 'map' to a new Map object.
    static copy(map) {
        Check.map("map", map)
        var newMap = {}
        for (key in map.keys) newMap[key] = map[key]
        return newMap
    }

    // Copies all elements from map1 and then map2 to a new Map object.
    // Keys in map1 which are also present in map2 have their values replaced
    // if 'replace' is true but not otherwise.
    // Returns the new Map.
    static union(map1, map2, replace) {
        Check.map("map1", map1)
        Check.map("map2", map2)
        Check.bool("replace", replace)
        var newMap = {}
        for (key in map1.keys) newMap[key] = map1[key]
        for (key in map2.keys) {
            if (!replace && map1.containsKey(key)) continue
            newMap[key] = map2[key]
        }
        return newMap
    }

    // Copies all elements for which map1 and map2 have keys in common to a new Map object.
    // Keys in map1 have their values replaced if 'replace' is true but not otherwise.
    // Returns the new Map.
    static intersect(map1, map2, replace) {
        Check.map("map1", map1)
        Check.map("map2", map2)
        Check.bool("replace", replace)
        var newMap = {}
        for (key in map1.keys) {
            if (!map2.containsKey(key)) continue
            newMap[key] = replace ? map2[key] : map1[key]
        }
        return newMap
     }

    // Copies all elements of map1 which do not have keys in common with map2 to a new Map object.
    static except(map1, map2) {
        Check.map("map1", map1)
        Check.map("map2", map2)
        var newMap = {}
        for (key in map1.keys) {
            if (!map2.containsKey(key)) newMap[key] = map1[key]
        }
        return newMap
    }

    // Returns whether or not map1 is a submap of map2.
    static isSubmap(map1, map2) {
        Check.map("map1", map1)
        Check.map("map2", map2)
        if (map1.count > map2.count) return false
        for (key in map1.keys) {
            if (!map2.containsKey(key)) return false
        }
        return true
    }

    // Returns whether or not map1 is a proper submap of map2.
    static isProperSubmap(map1, map2) {
        Check.map("map1", map1)
        Check.map("map2", map2)
        if (map1.count >= map2.count) return false
        for (key in map1.keys) {
            if (!map2.containsKey(key)) return false
        }
        return true
    }

    // Returns whether or not map1 is a supermap of map2.
    // A specialized version of 'containsAll'.
   static isSupermap(map1, map2) {
        Check.map("map1", map1)
        Check.map("map2", map2)
        if (map1.count <= map2.count) return false
        for (key in map2.keys) {
            if (!map1.containsKey(key)) return false
        }
        return true
    }

    // Returns whether two maps, map1 and map2, are the same length
    // and contain 'equal' elements, keys and values, using the '==' operator.
    static areEqual(map1, map2) {
        Check.map("map1", map1)
        Check.map("map2", map2)
        if (map1.count != map2.count) return false
        for (key in map1.keys) {
            if (!map2.containsKey(key) || map1[key] != map2[key]) return false
        }
        return true
    }

    // Returns whether two maps, map1 and map2, are the same length
    // and contain the same keys but not necessarily the same corresponding values.
    static sameKeys(map1, map2) {
        Check.map("map1", map1)
        Check.map("map2", map2)
        if (map1.count != map2.count) return false
        for (key in map1.keys) {
            if (!map2.containsKey(key)) return false
        }
        return true
    }

    // Sorts copies of the MapEntries of 'map' into key order using the default comparer
    // function: {|a, b| a < b } and returns a sequence (not list) of them.
    // Doesn't affect 'map' itself but enables it to be iterated in sorted order.
    static sort(map) {
        Check.map("map", map)
        return map.keys.toList.sort().map { |k| MapEntry.new(k, map[k]) }
    }

    // Sorts copies of the MapEntries of 'map' into key order using a
    // comparison function 'comparer' and returns a sequence (not list) of them.
    // Doesn't affect 'map' itself but enables it to be iterated in sorted order.
    static sort(map, comparer) {
        Check.map("map", map)
        return map.keys.toList.sort(comparer).map { |k| MapEntry.new(k, map[k]) }
    }
}

/* MultiSet treats a Map as if it were a Bag.*/
class MultiSet {
    // If 'key' exists in 'map' increases its value by the positive integer 'inc'.
    // Otherwise creates a new entry with that key and a value of 1.
    // Map values must be numeric. Returns 'map' after the change.
    static add(map, key, inc) {
        Check.map("map", map)
        Check.posInt("inc", inc)
        if (map.containsKey(key)) {
            map[key] = map[key] + inc
        } else {
            map[key] = 1
        }
        return map
    }

    // If 'key' exists in 'map' decreases its value by the positive integer 'dec'.
    // If the resulting value is <= 0, the entry is removed from the map.
    // Map values must be numeric. Returns 'map' after the change.
    static sub(map, key, dec) {
        Check.map("map", map)
        Check.posInt("dec", dec)
        if (map.containsKey(key)) {
            if (map[key] <= dec) {
                map.remove(key)
            } else {
                map[key] = map[key] - dec
            }
        }
        return map
    }

    // Convenience versions of the above methods where inc/dec is always 1.
    static add(map, key) { add(map, key, 1) }
    static sub(map, key) { sub(map, key, 1) }

    // Returns the total number of members (as opposed to elements) in 'map'.
    static count(map) {
        Check.map("map", map)
        var total = 0
        for (key in map.keys) total = total + map[key]
        return total
    }

    // Returns whether or not all values of 'map' are equal to 1
    static allDistinct(map) {
        Check.map("map", map)
        return map.values.all { |v| v == 1 }
    }
}