mirror of
https://github.com/aNNiMON/Own-Programming-Language-Tutorial.git
synced 2024-09-20 00:34:20 +03:00
[functional] Add tomap, Stream::toMap, Stream::anyMatch, Stream::allMatch, Stream::noneMatch
This commit is contained in:
parent
8ca3672204
commit
5a533cc6e1
@ -128,6 +128,17 @@ functions:
|
||||
]
|
||||
println groupby(data, def(e) = e.k1) // {"2"=[{k1=2, k2=x}], "4"=[{k1=4, k2=z}], "5"=[{k2=p, k1=5}]}
|
||||
println groupby(data, def(e) = e.k2) // {"x"=[{k1=2, k2=x}], "z"=[{k1=4, k2=z}], "p"=[{k2=p, k1=5}]}
|
||||
- name: tomap
|
||||
args: "data, keyMapper, valueMapper = def(v) = v, merger = def(oldValue, newValue) = newValue"
|
||||
desc: "converts elements of an array or a map to a map based on `keyMapper` and `valueMapper` functions result. `merger` function resolves collisions"
|
||||
desc_ru: "преобразует элементы массива или объекта в объект, основываясь на результате функций `keyMapper` и `valueMapper`. Функция `merger` используется для разрешения коллизий"
|
||||
since: 2.0.0
|
||||
example: |-
|
||||
use functional
|
||||
|
||||
data = ["apple", "banana"]
|
||||
println tomap(data, def(str) = str.substring(0, 1)) // {"a": "apple", "b": "banana"}
|
||||
println tomap(data, def(str) = str.substring(0, 1), ::toUpperCase) // {"a": "APPLE", "b": "BANANA"}
|
||||
- name: stream
|
||||
args: data
|
||||
desc: creates stream from data and returns `StreamValue`
|
||||
@ -229,6 +240,26 @@ types:
|
||||
args: ""
|
||||
desc: returns array of elements
|
||||
desc_ru: возвращает массив элементов
|
||||
- name: toMap
|
||||
args: "keyMapper, valueMapper = def(v) = v, merger = def(oldValue, newValue) = newValue"
|
||||
desc: "converts elements to a map based on `keyMapper` and `valueMapper` functions result. `merger` function resolves collisions"
|
||||
desc_ru: "преобразует элементы в объект, основываясь на результате функций `keyMapper` и `valueMapper`. Функция `merger` используется для разрешения коллизий"
|
||||
since: 2.0.0
|
||||
- name: anyMatch
|
||||
args: predicate
|
||||
desc: "returns `true` if there is any element matching the given `predicate`, otherwise returns `false`"
|
||||
desc_ru: "возвращает `true`, если хотя бы один элемент удовлетворяет функции `predicate`, иначе возвращает `false`"
|
||||
since: 2.0.0
|
||||
- name: allMatch
|
||||
args: predicate
|
||||
desc: "returns `true` if all elements match the given `predicate`, otherwise returns `false`"
|
||||
desc_ru: "возвращает `true`, если все элементы удовлетворяют функции `predicate`, иначе возвращает `false`"
|
||||
since: 2.0.0
|
||||
- name: noneMatch
|
||||
args: predicate
|
||||
desc: "returns `true` if no elements match the given `predicate`, otherwise returns `false`"
|
||||
desc_ru: "возвращает `true`, если нет элементов, удовлетворяющих функции `predicate`, иначе возвращает `false`"
|
||||
since: 2.0.0
|
||||
- name: count
|
||||
args: ""
|
||||
desc: returns the elements count
|
||||
|
@ -2,6 +2,7 @@ package com.annimon.ownlang.modules.functional;
|
||||
|
||||
import com.annimon.ownlang.exceptions.ArgumentsMismatchException;
|
||||
import com.annimon.ownlang.lib.*;
|
||||
import com.annimon.ownlang.modules.functional.functional_match.MatchType;
|
||||
import java.util.Arrays;
|
||||
|
||||
class StreamValue extends MapValue {
|
||||
@ -33,6 +34,10 @@ class StreamValue extends MapValue {
|
||||
set("forEachIndexed", wrapTerminal(new functional_forEachIndexed()));
|
||||
set("groupBy", wrapTerminal(new functional_groupBy()));
|
||||
set("toArray", args -> container);
|
||||
set("toMap", wrapTerminal(new functional_toMap()));
|
||||
set("anyMatch", wrapTerminal(functional_match.match(MatchType.ANY)));
|
||||
set("allMatch", wrapTerminal(functional_match.match(MatchType.ALL)));
|
||||
set("noneMatch", wrapTerminal(functional_match.match(MatchType.NONE)));
|
||||
set("joining", container::joinToString);
|
||||
set("count", args -> NumberValue.of(container.size()));
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ public final class functional implements Module {
|
||||
result.put("takewhile", new functional_takeWhile());
|
||||
result.put("dropwhile", new functional_dropWhile());
|
||||
result.put("groupby", new functional_groupBy());
|
||||
result.put("tomap", new functional_toMap());
|
||||
|
||||
result.put("chain", new functional_chain());
|
||||
result.put("stream", new functional_stream());
|
||||
|
@ -5,7 +5,7 @@ import com.annimon.ownlang.lib.Function;
|
||||
import com.annimon.ownlang.lib.Value;
|
||||
import com.annimon.ownlang.lib.ValueUtils;
|
||||
|
||||
public final class functional_chain implements Function {
|
||||
final class functional_chain implements Function {
|
||||
|
||||
@Override
|
||||
public Value execute(Value[] args) {
|
||||
|
@ -7,7 +7,7 @@ import com.annimon.ownlang.lib.FunctionValue;
|
||||
import com.annimon.ownlang.lib.Types;
|
||||
import com.annimon.ownlang.lib.Value;
|
||||
|
||||
public final class functional_combine implements Function {
|
||||
final class functional_combine implements Function {
|
||||
|
||||
@Override
|
||||
public Value execute(Value[] args) {
|
||||
|
@ -9,7 +9,7 @@ import com.annimon.ownlang.lib.Types;
|
||||
import com.annimon.ownlang.lib.Value;
|
||||
import com.annimon.ownlang.lib.ValueUtils;
|
||||
|
||||
public final class functional_dropWhile implements Function {
|
||||
final class functional_dropWhile implements Function {
|
||||
|
||||
@Override
|
||||
public Value execute(Value[] args) {
|
||||
|
@ -6,7 +6,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public final class functional_filter implements Function {
|
||||
final class functional_filter implements Function {
|
||||
|
||||
@Override
|
||||
public Value execute(Value[] args) {
|
||||
|
@ -2,7 +2,7 @@ package com.annimon.ownlang.modules.functional;
|
||||
|
||||
import com.annimon.ownlang.lib.*;
|
||||
|
||||
public final class functional_filterNot implements Function {
|
||||
final class functional_filterNot implements Function {
|
||||
|
||||
@Override
|
||||
public Value execute(Value[] args) {
|
||||
|
@ -10,7 +10,7 @@ import com.annimon.ownlang.lib.ValueUtils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class functional_flatmap implements Function {
|
||||
final class functional_flatmap implements Function {
|
||||
|
||||
@Override
|
||||
public Value execute(Value[] args) {
|
||||
|
@ -4,7 +4,7 @@ import com.annimon.ownlang.exceptions.TypeException;
|
||||
import com.annimon.ownlang.lib.*;
|
||||
import java.util.Map;
|
||||
|
||||
public final class functional_forEach implements Function {
|
||||
final class functional_forEach implements Function {
|
||||
|
||||
@Override
|
||||
public Value execute(Value[] args) {
|
||||
|
@ -3,7 +3,7 @@ package com.annimon.ownlang.modules.functional;
|
||||
import com.annimon.ownlang.exceptions.TypeException;
|
||||
import com.annimon.ownlang.lib.*;
|
||||
|
||||
public final class functional_forEachIndexed implements Function {
|
||||
final class functional_forEachIndexed implements Function {
|
||||
|
||||
@Override
|
||||
public Value execute(Value[] args) {
|
||||
|
@ -7,7 +7,7 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public final class functional_groupBy implements Function {
|
||||
final class functional_groupBy implements Function {
|
||||
|
||||
@Override
|
||||
public Value execute(Value[] args) {
|
||||
|
@ -10,7 +10,7 @@ import com.annimon.ownlang.lib.Value;
|
||||
import com.annimon.ownlang.lib.ValueUtils;
|
||||
import java.util.Map;
|
||||
|
||||
public final class functional_map implements Function {
|
||||
final class functional_map implements Function {
|
||||
|
||||
@Override
|
||||
public Value execute(Value[] args) {
|
||||
|
@ -0,0 +1,52 @@
|
||||
package com.annimon.ownlang.modules.functional;
|
||||
|
||||
import com.annimon.ownlang.exceptions.TypeException;
|
||||
import com.annimon.ownlang.lib.*;
|
||||
|
||||
final class functional_match {
|
||||
|
||||
enum MatchType { ALL, ANY, NONE }
|
||||
|
||||
static Function match(MatchType matchType) {
|
||||
return args -> {
|
||||
Arguments.check(2, args.length);
|
||||
final Value container = args[0];
|
||||
if (container.type() != Types.ARRAY) {
|
||||
throw new TypeException("Invalid first argument. Array expected");
|
||||
}
|
||||
final Function predicate = ValueUtils.consumeFunction(args[1], 1);
|
||||
return switch (matchType) {
|
||||
case ALL -> allMatch((ArrayValue) container, predicate);
|
||||
case ANY -> anyMatch((ArrayValue) container, predicate);
|
||||
case NONE -> noneMatch((ArrayValue) container, predicate);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
private static NumberValue allMatch(ArrayValue array, Function predicate) {
|
||||
for (Value value : array) {
|
||||
if (predicate.execute(value) == NumberValue.ZERO) {
|
||||
return NumberValue.ZERO;
|
||||
}
|
||||
}
|
||||
return NumberValue.ONE;
|
||||
}
|
||||
|
||||
private static NumberValue anyMatch(ArrayValue array, Function predicate) {
|
||||
for (Value value : array) {
|
||||
if (predicate.execute(value) != NumberValue.ZERO) {
|
||||
return NumberValue.ONE;
|
||||
}
|
||||
}
|
||||
return NumberValue.ZERO;
|
||||
}
|
||||
|
||||
private static NumberValue noneMatch(ArrayValue array, Function predicate) {
|
||||
for (Value value : array) {
|
||||
if (predicate.execute(value) != NumberValue.ZERO) {
|
||||
return NumberValue.ZERO;
|
||||
}
|
||||
}
|
||||
return NumberValue.ONE;
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@ import com.annimon.ownlang.lib.Value;
|
||||
import com.annimon.ownlang.lib.ValueUtils;
|
||||
import java.util.Map;
|
||||
|
||||
public final class functional_reduce implements Function {
|
||||
final class functional_reduce implements Function {
|
||||
|
||||
@Override
|
||||
public Value execute(Value[] args) {
|
||||
|
@ -10,7 +10,7 @@ import com.annimon.ownlang.lib.ValueUtils;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
public final class functional_sortBy implements Function {
|
||||
final class functional_sortBy implements Function {
|
||||
|
||||
@Override
|
||||
public Value execute(Value[] args) {
|
||||
|
@ -3,7 +3,7 @@ package com.annimon.ownlang.modules.functional;
|
||||
import com.annimon.ownlang.exceptions.TypeException;
|
||||
import com.annimon.ownlang.lib.*;
|
||||
|
||||
public final class functional_stream implements Function {
|
||||
final class functional_stream implements Function {
|
||||
|
||||
@Override
|
||||
public Value execute(Value[] args) {
|
||||
|
@ -6,7 +6,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public final class functional_takeWhile implements Function {
|
||||
final class functional_takeWhile implements Function {
|
||||
|
||||
@Override
|
||||
public Value execute(Value[] args) {
|
||||
|
@ -0,0 +1,63 @@
|
||||
package com.annimon.ownlang.modules.functional;
|
||||
|
||||
import com.annimon.ownlang.exceptions.TypeException;
|
||||
import com.annimon.ownlang.lib.*;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
final class functional_toMap implements Function {
|
||||
|
||||
@Override
|
||||
public Value execute(Value[] args) {
|
||||
Arguments.checkRange(2, 4, args.length);
|
||||
final Value container = args[0];
|
||||
final Function keyMapper = ValueUtils.consumeFunction(args[1], 1);
|
||||
final Function valueMapper = args.length >= 3
|
||||
? ValueUtils.consumeFunction(args[2], 2)
|
||||
: null;
|
||||
final Function merger = args.length >= 4
|
||||
? ValueUtils.consumeFunction(args[3], 3)
|
||||
: null;
|
||||
return toMap(container, keyMapper, valueMapper, merger);
|
||||
}
|
||||
|
||||
static MapValue toMap(Value container, Function keyMapper, Function valueMapper, Function merger) {
|
||||
return switch (container.type()) {
|
||||
case Types.ARRAY -> toMap((ArrayValue) container, keyMapper, valueMapper, merger);
|
||||
case Types.MAP -> toMap((MapValue) container, keyMapper, valueMapper, merger);
|
||||
default -> throw new TypeException("Cannot iterate " + Types.typeToString(container.type()));
|
||||
};
|
||||
}
|
||||
|
||||
static MapValue toMap(ArrayValue array, Function keyMapper, Function valueMapper, Function merger) {
|
||||
final Map<Value, Value> result = new LinkedHashMap<>(array.size());
|
||||
for (Value element : array) {
|
||||
final Value key = keyMapper.execute(element);
|
||||
final Value value = valueMapper != null
|
||||
? valueMapper.execute(element)
|
||||
: element;
|
||||
final Value oldValue = result.get(key);
|
||||
final Value newValue = (oldValue == null || merger == null)
|
||||
? value
|
||||
: merger.execute(oldValue, value);
|
||||
result.put(key, newValue);
|
||||
}
|
||||
return new MapValue(result);
|
||||
}
|
||||
|
||||
static MapValue toMap(MapValue map, Function keyMapper, Function valueMapper, Function merger) {
|
||||
final Map<Value, Value> result = new LinkedHashMap<>(map.size());
|
||||
for (Map.Entry<Value, Value> element : map) {
|
||||
final Value key = keyMapper.execute(element.getKey(), element.getValue());
|
||||
final Value value = valueMapper != null
|
||||
? valueMapper.execute(element.getKey(), element.getValue())
|
||||
: element.getValue();
|
||||
final Value oldValue = result.get(key);
|
||||
final Value newValue = (oldValue == null || merger == null)
|
||||
? value
|
||||
: merger.execute(oldValue, value);
|
||||
result.put(key, newValue);
|
||||
}
|
||||
return new MapValue(result);
|
||||
}
|
||||
}
|
@ -54,6 +54,9 @@ public final class FunctionalExpression extends InterruptableNode
|
||||
|
||||
private Function consumeFunction(Node expr) {
|
||||
final Value value = expr.eval();
|
||||
if (value == null) {
|
||||
throw new UnknownFunctionException(expr.toString(), getRange());
|
||||
}
|
||||
if (value.type() == Types.FUNCTION) {
|
||||
return ((FunctionValue) value).getValue();
|
||||
}
|
||||
|
@ -125,3 +125,32 @@ def testMapsGroupBy() {
|
||||
assertEquals([["test1", 234], ["test2", 345], ["test3", 456]], sort(result[true]))
|
||||
assertEquals([["abc", 123], ["def", 567]], sort(result[false]))
|
||||
}
|
||||
|
||||
def testToMap() {
|
||||
data = ["apple", "banana", "cherry"]
|
||||
result = stream(data)
|
||||
.toMap(def(str) = str.substring(0, 1), ::toUpperCase)
|
||||
assertEquals("APPLE", result.a)
|
||||
assertEquals("BANANA", result.b)
|
||||
assertEquals("CHERRY", result.c)
|
||||
}
|
||||
|
||||
def testAllMatch() {
|
||||
data = [2, 4, 8, 20]
|
||||
assertTrue(stream(data).allMatch(def(v) = v % 2 == 0))
|
||||
assertFalse(stream(data).allMatch(def(v) = v < 10))
|
||||
}
|
||||
|
||||
def testAnyMatch() {
|
||||
data = [2, 4, 8, 20]
|
||||
assertTrue(stream(data).anyMatch(def(v) = v > 10))
|
||||
assertFalse(stream(data).anyMatch(def(v) = v % 2 == 1))
|
||||
}
|
||||
|
||||
def testNoneMatch() {
|
||||
data = [2, 4, 8, 20]
|
||||
assertTrue(stream(data).noneMatch(def(v) = v % 2 == 1))
|
||||
assertFalse(stream(data).noneMatch(def(v) = v > 10))
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,39 @@
|
||||
use std, functional
|
||||
|
||||
def testArrayToMapByKeyMapper() {
|
||||
data = ["apple", "banana", "cherry"]
|
||||
result = tomap(data, def(str) = str.substring(0, 1))
|
||||
assertEquals("apple", result.a)
|
||||
assertEquals("banana", result.b)
|
||||
assertEquals("cherry", result.c)
|
||||
}
|
||||
|
||||
def testArrayToMapByKeyValueMapper() {
|
||||
data = ["apple", "banana", "cherry"]
|
||||
result = tomap(data, def(str) = str.substring(0, 1), ::toUpperCase)
|
||||
assertEquals("APPLE", result.a)
|
||||
assertEquals("BANANA", result.b)
|
||||
assertEquals("CHERRY", result.c)
|
||||
}
|
||||
|
||||
def testArrayToMapByKeyValueMapperAndMerger() {
|
||||
data = ["apple", "banana", "cherry", "apricot", "coconut"]
|
||||
result = tomap(data, def(str) = str.substring(0, 1), ::toUpperCase, def(oldValue, newValue) = oldValue + ", " + newValue)
|
||||
assertEquals("APPLE, APRICOT", result.a)
|
||||
assertEquals("BANANA", result.b)
|
||||
assertEquals("CHERRY, COCONUT", result.c)
|
||||
}
|
||||
|
||||
def testMapToMapByKeyMapper() {
|
||||
data = {"k1": 1, "k2": 2}
|
||||
result = tomap(data, def(k, v) = k + "" + v)
|
||||
assertEquals(1, result.k11)
|
||||
assertEquals(2, result.k22)
|
||||
}
|
||||
|
||||
def testMapToMapByKeyValueMapper() {
|
||||
data = {"k1": 1, "k2": 2}
|
||||
result = tomap(data, def(k, v) = k + "" + v, def(k, v) = v + 10)
|
||||
assertEquals(11, result.k11)
|
||||
assertEquals(12, result.k22)
|
||||
}
|
Loading…
Reference in New Issue
Block a user