Skip to content

Commit 110aa62

Browse files
committed
[functional] Add Stream.filterNot
1 parent fb6a8b8 commit 110aa62

File tree

7 files changed

+209
-138
lines changed

7 files changed

+209
-138
lines changed
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package com.annimon.ownlang.modules.functional;
2+
3+
import com.annimon.ownlang.exceptions.ArgumentsMismatchException;
4+
import com.annimon.ownlang.lib.*;
5+
import java.util.Arrays;
6+
7+
class StreamValue extends MapValue {
8+
9+
private final ArrayValue container;
10+
11+
public StreamValue(ArrayValue container) {
12+
super(16);
13+
this.container = container;
14+
init();
15+
}
16+
17+
private void init() {
18+
set("filter", wrapIntermediate(new functional_filter()));
19+
set("filterNot", wrapIntermediate(new functional_filterNot()));
20+
set("map", wrapIntermediate(new functional_map()));
21+
set("flatMap", wrapIntermediate(new functional_flatmap()));
22+
set("sorted", this::sorted);
23+
set("sortBy", wrapIntermediate(new functional_sortby()));
24+
set("takeWhile", wrapIntermediate(new functional_takeWhile()));
25+
set("dropWhile", wrapIntermediate(new functional_dropwhile()));
26+
set("peek", wrapIntermediate(new functional_foreach()));
27+
set("skip", this::skip);
28+
set("limit", this::limit);
29+
set("custom", this::custom);
30+
31+
set("reduce", wrapTerminal(new functional_reduce()));
32+
set("forEach", wrapTerminal(new functional_foreach()));
33+
set("toArray", args -> container);
34+
set("joining", container::joinToString);
35+
set("count", args -> NumberValue.of(container.size()));
36+
}
37+
38+
private Value skip(Value[] args) {
39+
Arguments.check(1, args.length);
40+
41+
final int skipCount = args[0].asInt();
42+
final int size = container.size();
43+
44+
if (skipCount <= 0) return this;
45+
if (skipCount >= size) {
46+
return new StreamValue(new ArrayValue(0));
47+
}
48+
49+
final Value[] result = new Value[size - skipCount];
50+
System.arraycopy(container.getCopyElements(), skipCount, result, 0, result.length);
51+
return new StreamValue(new ArrayValue(result));
52+
}
53+
54+
private Value limit(Value[] args) {
55+
Arguments.check(1, args.length);
56+
57+
final int limitCount = args[0].asInt();
58+
final int size = container.size();
59+
60+
if (limitCount >= size) return this;
61+
if (limitCount <= 0) {
62+
return new StreamValue(new ArrayValue(0));
63+
}
64+
65+
final Value[] result = new Value[limitCount];
66+
System.arraycopy(container.getCopyElements(), 0, result, 0, limitCount);
67+
return new StreamValue(new ArrayValue(result));
68+
}
69+
70+
private Value sorted(Value[] args) {
71+
Arguments.checkOrOr(0, 1, args.length);
72+
final Value[] elements = container.getCopyElements();
73+
74+
switch (args.length) {
75+
case 0 -> Arrays.sort(elements);
76+
case 1 -> {
77+
final Function comparator = ValueUtils.consumeFunction(args[0], 0);
78+
Arrays.sort(elements, (o1, o2) -> comparator.execute(o1, o2).asInt());
79+
}
80+
default -> throw new ArgumentsMismatchException("Wrong number of arguments");
81+
}
82+
83+
return new StreamValue(new ArrayValue(elements));
84+
}
85+
86+
private Value custom(Value[] args) {
87+
Arguments.check(1, args.length);
88+
final Function f = ValueUtils.consumeFunction(args[0], 0);
89+
final Value result = f.execute(container);
90+
if (result.type() == Types.ARRAY) {
91+
return new StreamValue((ArrayValue) result);
92+
}
93+
return result;
94+
}
95+
96+
private FunctionValue wrapIntermediate(Function f) {
97+
return wrap(f, true);
98+
}
99+
100+
private FunctionValue wrapTerminal(Function f) {
101+
return wrap(f, false);
102+
}
103+
104+
private FunctionValue wrap(Function f, boolean intermediate) {
105+
return new FunctionValue(args -> {
106+
final Value[] newArgs = new Value[args.length + 1];
107+
System.arraycopy(args, 0, newArgs, 1, args.length);
108+
newArgs[0] = container;
109+
final Value result = f.execute(newArgs);
110+
if (intermediate && result.type() == Types.ARRAY) {
111+
return new StreamValue((ArrayValue) result);
112+
}
113+
return result;
114+
});
115+
}
116+
}

modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package com.annimon.ownlang.modules.functional;
22

3-
import com.annimon.ownlang.lib.Function;
4-
import com.annimon.ownlang.lib.FunctionValue;
5-
import com.annimon.ownlang.lib.Value;
3+
import com.annimon.ownlang.lib.*;
64
import com.annimon.ownlang.modules.Module;
75
import java.util.HashMap;
86
import java.util.Map;
@@ -25,9 +23,9 @@ public Map<String, Function> functions() {
2523
result.put("map", new functional_map());
2624
result.put("flatmap", new functional_flatmap());
2725
result.put("reduce", new functional_reduce());
28-
result.put("filter", new functional_filter(false));
26+
result.put("filter", new functional_filter());
2927
result.put("sortby", new functional_sortby());
30-
result.put("takewhile", new functional_filter(true));
28+
result.put("takewhile", new functional_takeWhile());
3129
result.put("dropwhile", new functional_dropwhile());
3230

3331
result.put("chain", new functional_chain());

modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,46 +8,41 @@
88

99
public final class functional_filter implements Function {
1010

11-
private final boolean takeWhile;
12-
13-
public functional_filter(boolean takeWhile) {
14-
this.takeWhile = takeWhile;
15-
}
16-
1711
@Override
1812
public Value execute(Value[] args) {
1913
Arguments.check(2, args.length);
2014
final Value container = args[0];
2115
final Function predicate = ValueUtils.consumeFunction(args[1], 1);
16+
return filter(container, predicate);
17+
}
18+
19+
static Value filter(Value container, Function predicate) {
2220
if (container.type() == Types.ARRAY) {
23-
return filterArray((ArrayValue) container, predicate, takeWhile);
21+
return filterArray((ArrayValue) container, predicate);
2422
}
25-
2623
if (container.type() == Types.MAP) {
27-
return filterMap((MapValue) container, predicate, takeWhile);
24+
return filterMap((MapValue) container, predicate);
2825
}
29-
3026
throw new TypeException("Invalid first argument. Array or map expected");
3127
}
32-
33-
private Value filterArray(ArrayValue array, Function predicate, boolean takeWhile) {
28+
29+
static ArrayValue filterArray(ArrayValue array, Function predicate) {
3430
final int size = array.size();
3531
final List<Value> values = new ArrayList<>(size);
3632
for (Value value : array) {
3733
if (predicate.execute(value) != NumberValue.ZERO) {
3834
values.add(value);
39-
} else if (takeWhile) break;
35+
}
4036
}
41-
final int newSize = values.size();
42-
return new ArrayValue(values.toArray(new Value[newSize]));
37+
return new ArrayValue(values);
4338
}
44-
45-
private Value filterMap(MapValue map, Function predicate, boolean takeWhile) {
39+
40+
static MapValue filterMap(MapValue map, Function predicate) {
4641
final MapValue result = new MapValue(map.size());
4742
for (Map.Entry<Value, Value> element : map) {
4843
if (predicate.execute(element.getKey(), element.getValue()) != NumberValue.ZERO) {
4944
result.set(element.getKey(), element.getValue());
50-
} else if (takeWhile) break;
45+
}
5146
}
5247
return result;
5348
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.annimon.ownlang.modules.functional;
2+
3+
import com.annimon.ownlang.lib.*;
4+
5+
public final class functional_filterNot implements Function {
6+
7+
@Override
8+
public Value execute(Value[] args) {
9+
Arguments.check(2, args.length);
10+
final Value container = args[0];
11+
final Function predicate = ValueUtils.consumeFunction(args[1], 1);
12+
return functional_filter.filter(container, negate(predicate));
13+
}
14+
15+
static Function negate(Function f) {
16+
return args -> NumberValue.fromBoolean(f.execute(args) == NumberValue.ZERO);
17+
}
18+
}
Lines changed: 0 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package com.annimon.ownlang.modules.functional;
22

3-
import com.annimon.ownlang.exceptions.ArgumentsMismatchException;
43
import com.annimon.ownlang.exceptions.TypeException;
54
import com.annimon.ownlang.lib.*;
6-
import java.util.Arrays;
75

86
public final class functional_stream implements Function {
97

@@ -25,117 +23,4 @@ public Value execute(Value[] args) {
2523
throw new TypeException("Invalid argument. Array or map expected");
2624
}
2725
}
28-
29-
private static class StreamValue extends MapValue {
30-
31-
private final ArrayValue container;
32-
33-
public StreamValue(ArrayValue container) {
34-
super(16);
35-
this.container = container;
36-
init();
37-
}
38-
39-
private void init() {
40-
set("filter", wrapIntermediate(new functional_filter(false)));
41-
set("map", wrapIntermediate(new functional_map()));
42-
set("flatMap", wrapIntermediate(new functional_flatmap()));
43-
set("sorted", this::sorted);
44-
set("sortBy", wrapIntermediate(new functional_sortby()));
45-
set("takeWhile", wrapIntermediate(new functional_filter(true)));
46-
set("dropWhile", wrapIntermediate(new functional_dropwhile()));
47-
set("peek", wrapIntermediate(new functional_foreach()));
48-
set("skip", this::skip);
49-
set("limit", this::limit);
50-
set("custom", this::custom);
51-
52-
set("reduce", wrapTerminal(new functional_reduce()));
53-
set("forEach", wrapTerminal(new functional_foreach()));
54-
set("toArray", args -> container);
55-
set("joining", container::joinToString);
56-
set("count", args -> NumberValue.of(container.size()));
57-
}
58-
59-
private Value skip(Value[] args) {
60-
Arguments.check(1, args.length);
61-
62-
final int skipCount = args[0].asInt();
63-
final int size = container.size();
64-
65-
if (skipCount <= 0) return this;
66-
if (skipCount >= size) {
67-
return new StreamValue(new ArrayValue(0));
68-
}
69-
70-
final Value[] result = new Value[size - skipCount];
71-
System.arraycopy(container.getCopyElements(), skipCount, result, 0, result.length);
72-
return new StreamValue(new ArrayValue(result));
73-
}
74-
75-
private Value limit(Value[] args) {
76-
Arguments.check(1, args.length);
77-
78-
final int limitCount = args[0].asInt();
79-
final int size = container.size();
80-
81-
if (limitCount >= size) return this;
82-
if (limitCount <= 0) {
83-
return new StreamValue(new ArrayValue(0));
84-
}
85-
86-
final Value[] result = new Value[limitCount];
87-
System.arraycopy(container.getCopyElements(), 0, result, 0, limitCount);
88-
return new StreamValue(new ArrayValue(result));
89-
}
90-
91-
private Value sorted(Value[] args) {
92-
Arguments.checkOrOr(0, 1, args.length);
93-
final Value[] elements = container.getCopyElements();
94-
95-
switch (args.length) {
96-
case 0:
97-
Arrays.sort(elements);
98-
break;
99-
case 1:
100-
final Function comparator = ValueUtils.consumeFunction(args[0], 0);
101-
Arrays.sort(elements, (o1, o2) -> comparator.execute(o1, o2).asInt());
102-
break;
103-
default:
104-
throw new ArgumentsMismatchException("Wrong number of arguments");
105-
}
106-
107-
return new StreamValue(new ArrayValue(elements));
108-
}
109-
110-
private Value custom(Value[] args) {
111-
Arguments.check(1, args.length);
112-
final Function f = ValueUtils.consumeFunction(args[0], 0);
113-
final Value result = f.execute(container);
114-
if (result.type() == Types.ARRAY) {
115-
return new StreamValue((ArrayValue) result);
116-
}
117-
return result;
118-
}
119-
120-
private FunctionValue wrapIntermediate(Function f) {
121-
return wrap(f, true);
122-
}
123-
124-
private FunctionValue wrapTerminal(Function f) {
125-
return wrap(f, false);
126-
}
127-
128-
private FunctionValue wrap(Function f, boolean intermediate) {
129-
return new FunctionValue(args -> {
130-
final Value[] newArgs = new Value[args.length + 1];
131-
System.arraycopy(args, 0, newArgs, 1, args.length);
132-
newArgs[0] = container;
133-
final Value result = f.execute(newArgs);
134-
if (intermediate && result.type() == Types.ARRAY) {
135-
return new StreamValue((ArrayValue) result);
136-
}
137-
return result;
138-
});
139-
}
140-
}
14126
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.annimon.ownlang.modules.functional;
2+
3+
import com.annimon.ownlang.exceptions.TypeException;
4+
import com.annimon.ownlang.lib.*;
5+
import java.util.ArrayList;
6+
import java.util.List;
7+
import java.util.Map;
8+
9+
public final class functional_takeWhile implements Function {
10+
11+
@Override
12+
public Value execute(Value[] args) {
13+
Arguments.check(2, args.length);
14+
final Value container = args[0];
15+
final Function predicate = ValueUtils.consumeFunction(args[1], 1);
16+
return takeWhile(container, predicate);
17+
}
18+
19+
static Value takeWhile(Value container, Function predicate) {
20+
if (container.type() == Types.ARRAY) {
21+
return takeWhileArray((ArrayValue) container, predicate);
22+
}
23+
if (container.type() == Types.MAP) {
24+
return takeWhileMap((MapValue) container, predicate);
25+
}
26+
throw new TypeException("Invalid first argument. Array or map expected");
27+
}
28+
29+
static ArrayValue takeWhileArray(ArrayValue array, Function predicate) {
30+
final int size = array.size();
31+
final List<Value> values = new ArrayList<>(size);
32+
for (Value value : array) {
33+
if (predicate.execute(value) != NumberValue.ZERO) {
34+
values.add(value);
35+
} else break;
36+
}
37+
return new ArrayValue(values);
38+
}
39+
40+
static MapValue takeWhileMap(MapValue map, Function predicate) {
41+
final MapValue result = new MapValue(map.size());
42+
for (Map.Entry<Value, Value> element : map) {
43+
if (predicate.execute(element.getKey(), element.getValue()) != NumberValue.ZERO) {
44+
result.set(element.getKey(), element.getValue());
45+
} else break;
46+
}
47+
return result;
48+
}
49+
}

0 commit comments

Comments
 (0)