This is an automated email from the ASF dual-hosted git repository. paulk pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push: new 40b012ce9b GROOVY-11351: Add zip and columns extension methods 40b012ce9b is described below commit 40b012ce9b84ec8ba1864a21e9319fe2cc084358 Author: Paul King <pa...@asert.com.au> AuthorDate: Thu Mar 28 12:57:20 2024 +1000 GROOVY-11351: Add zip and columns extension methods --- .../groovy/runtime/ArrayGroovyMethods.java | 136 +++++++++++++++++++++ .../groovy/runtime/DefaultGroovyMethods.java | 65 ++++++++++ .../util/DoubleDoubleArrayColumnIterator.java | 69 +++++++++++ .../groovy/util/FloatFloatArrayColumnIterator.java | 69 +++++++++++ .../groovy/util/IntIntArrayColumnIterator.java | 69 +++++++++++ .../groovy/util/LongLongArrayColumnIterator.java | 69 +++++++++++ 6 files changed, 477 insertions(+) diff --git a/src/main/java/org/codehaus/groovy/runtime/ArrayGroovyMethods.java b/src/main/java/org/codehaus/groovy/runtime/ArrayGroovyMethods.java index d3b7b9f96d..acf07d62ce 100644 --- a/src/main/java/org/codehaus/groovy/runtime/ArrayGroovyMethods.java +++ b/src/main/java/org/codehaus/groovy/runtime/ArrayGroovyMethods.java @@ -26,6 +26,7 @@ import groovy.lang.MetaClass; import groovy.lang.ObjectRange; import groovy.lang.Range; import groovy.lang.SpreadMap; +import groovy.lang.Tuple2; import groovy.transform.stc.ClosureParams; import groovy.transform.stc.FirstParam; import groovy.transform.stc.FromString; @@ -46,11 +47,15 @@ import org.codehaus.groovy.util.ByteArrayIterator; import org.codehaus.groovy.util.CharArrayIterator; import org.codehaus.groovy.util.DoubleArrayIterable; import org.codehaus.groovy.util.DoubleArrayIterator; +import org.codehaus.groovy.util.DoubleDoubleArrayColumnIterator; import org.codehaus.groovy.util.FloatArrayIterator; +import org.codehaus.groovy.util.FloatFloatArrayColumnIterator; import org.codehaus.groovy.util.IntArrayIterable; import org.codehaus.groovy.util.IntArrayIterator; +import org.codehaus.groovy.util.IntIntArrayColumnIterator; import org.codehaus.groovy.util.LongArrayIterable; import org.codehaus.groovy.util.LongArrayIterator; +import org.codehaus.groovy.util.LongLongArrayColumnIterator; import org.codehaus.groovy.util.ShortArrayIterator; import java.lang.reflect.Array; @@ -75,6 +80,7 @@ import java.util.function.Consumer; import java.util.function.DoubleConsumer; import java.util.function.DoubleUnaryOperator; import java.util.function.Function; +import java.util.function.IntBinaryOperator; import java.util.function.IntConsumer; import java.util.function.IntUnaryOperator; import java.util.function.LongConsumer; @@ -1076,6 +1082,67 @@ public class ArrayGroovyMethods extends DefaultGroovyMethodsSupport { return DefaultGroovyMethods.collectMany(new ArrayIterable<>(self), collector, projection); } + //-------------------------------------------------------------------------- + // columns + + /** + * An iterator of the columns of the array. + * <pre class="groovyTestCase"> + * double[][] nums = [[1.0d, 2.0d], [10.0d, 20.0d]] + * assert nums.transpose() == nums.columns().toList() + * </pre> + * + * @param self a double[][] + * @return the iterator + */ + public static Iterator<double[]> columns(double[][] self) { + return new DoubleDoubleArrayColumnIterator(self); + } + + /** + * An iterator of the columns of the array. + * <pre class="groovyTestCase"> + * float[][] nums = [[1.0f, 2.0f], [10.0f, 20.0f]] + * assert nums.transpose() == nums.columns().toList() + * </pre> + * + * @param self a float[][] + * @return the iterator + */ + public static Iterator<float[]> columns(float[][] self) { + return new FloatFloatArrayColumnIterator(self); + } + + /** + * An iterator of the columns of the array. + * <pre class="groovyTestCase"> + * int[][] nums = [[1, 2], [10, 20]] + * assert nums.transpose() == nums.columns().toList() + * assert nums.columns().collect{ int[] col -> col.sum() } == [11, 22] + * </pre> + * + * @param self an int[][] + * @return the iterator + */ + public static Iterator<int[]> columns(int[][] self) { + return new IntIntArrayColumnIterator(self); + } + + /** + * An iterator of the columns of the array. + * <pre class="groovyTestCase"> + * long[][] nums = [[1L, 2L], [10L, 20L]] + * assert nums.transpose() == nums.columns().toList() + * assert nums.columns().collect{ long[] col -> col.sum() } == [11L, 22L] + * </pre> + * + * @param self a long[][] + * @return the iterator + */ + public static Iterator<long[]> columns(long[][] self) { + return new LongLongArrayColumnIterator(self); + } + //-------------------------------------------------------------------------- // contains @@ -8800,6 +8867,75 @@ public class ArrayGroovyMethods extends DefaultGroovyMethodsSupport { return DefaultGroovyMethods.collectEntries(new ArrayIterator<>(keys), collector, Function.identity(), valueTransform); } + //-------------------------------------------------------------------------- + // zip + + /** + * An iterator of all the pairs of two arrays. + * <pre class="groovyTestCase"> + * double[] small = [1.0d, 2.0d, 3.0d] + * double[] large = [100d, 200d, 300d] + * assert [small, large].transpose() == small.zip(large).toList() + * </pre> + * + * @param self a double[] + * @param other another double[] + * @return an iterator of all the pairs from self and other + */ + public static Iterator<Tuple2<Double, Double>> zip(double[] self, double[] other) { + return DefaultGroovyMethods.zip(new DoubleArrayIterator(self), new DoubleArrayIterator(other)); + } + + /** + * An iterator of all the pairs of two arrays. + * <pre class="groovyTestCase"> + * float[] small = [1.0f, 2.0f, 3.0f] + * float[] large = [100f, 200f, 300f] + * assert [small, large].transpose() == small.zip(large).toList() + * </pre> + * + * @param self a float[] + * @param other another float[] + * @return an iterator of all the pairs from self and other + */ + public static Iterator<Tuple2<Float, Float>> zip(float[] self, float[] other) { + return DefaultGroovyMethods.zip(new FloatArrayIterator(self), new FloatArrayIterator(other)); + } + + /** + * An iterator of all the pairs of two arrays. + * <pre class="groovyTestCase"> + * int[] small = [1, 2, 3] + * int[] large = [100, 200, 300] + * assert [101, 202, 303] == small.zip(large).collect{ a, b -> a + b } + * assert [small, large].transpose() == small.zip(large).toList() + * </pre> + * + * @param self an int[] + * @param other another int[] + * @return an iterator of all the pairs from self and other + */ + public static Iterator<Tuple2<Integer, Integer>> zip(int[] self, int[] other) { + return DefaultGroovyMethods.zip(new IntArrayIterator(self), new IntArrayIterator(other)); + } + + /** + * An iterator of all the pairs of two arrays. + * <pre class="groovyTestCase"> + * long[] small = [1L, 2L, 3L] + * long[] large = [100L, 200L, 300L] + * assert [101L, 202L, 303L] == small.zip(large).collect{ a, b -> a + b } + * assert [small, large].transpose() == small.zip(large).toList() + * </pre> + * + * @param self a long[] + * @param other another long[] + * @return an iterator of all the pairs from self and other + */ + public static Iterator<Tuple2<Long, Long>> zip(long[] self, long[] other) { + return DefaultGroovyMethods.zip(new LongArrayIterator(self), new LongArrayIterator(other)); + } + //-------------------------------------------------------------------------- /** diff --git a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java index e2ed576897..cd366b4e15 100644 --- a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java +++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java @@ -15885,6 +15885,71 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport { } } + //-------------------------------------------------------------------------- + // zip + + /** + * An iterator of all the pairs of two Iterables. + * <pre class="groovyTestCase"> + * def one = ['cat', 'spider'] + * def two = ['fish', 'monkey'] + * assert ['catfish', 'spidermonkey'] == one.zip(two).collect{ a, b -> a + b } + * assert [one, two].transpose() == one.zip(two).toList() + * </pre> + * + * @param self an Iterable + * @param other another Iterable + * @return an iterator of all the pairs from self and other + * @since 5.0.0 + */ + public static <U, V> Iterator<Tuple2<U, V>> zip(Iterable<U> self, Iterable<V> other) { + return zip(self.iterator(), other.iterator()); + } + + /** + * An iterator of all the pairs of two Iterators. + * <pre class="groovyTestCase"> + * def small = [1, 2, 3].iterator() + * def large = [100, 200, 300].iterator() + * assert [101, 202, 303] == small.zip(large).collect{ a, b -> a + b } + * assert [small.toList(), large.toList()].transpose() == small.zip(large).toList() + * </pre> + * + * @param self an Iterator + * @param other another Iterator + * @return an iterator of all the pairs from self and other + * @since 5.0.0 + */ + public static <U, V> Iterator<Tuple2<U, V>> zip(Iterator<U> self, Iterator<V> other) { + return new ZipIterator<>(self, other); + } + + private static final class ZipIterator<U, V> implements Iterator<Tuple2<U, V>> { + private final Iterator<U> delegate; + private final Iterator<V> other; + + private ZipIterator(Iterator<U> delegate, Iterator<V> other) { + this.delegate = delegate; + this.other = other; + } + + @Override + public boolean hasNext() { + return delegate.hasNext() && other.hasNext(); + } + + @Override + public Tuple2<U, V> next() { + if (!hasNext()) throw new NoSuchElementException(); + return new Tuple2<>(delegate.next(), other.next()); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + //-------------------------------------------------------------------------- // withTraits diff --git a/src/main/java/org/codehaus/groovy/util/DoubleDoubleArrayColumnIterator.java b/src/main/java/org/codehaus/groovy/util/DoubleDoubleArrayColumnIterator.java new file mode 100644 index 0000000000..e6b313a025 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/util/DoubleDoubleArrayColumnIterator.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.groovy.util; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Objects; + +/** + * An iterator providing the columns of a double[][]. + * In the case of an array with different size rows, + * the number of columns will be equal to the length of the smallest row. + * + * @since 5.0.0 + */ +public class DoubleDoubleArrayColumnIterator implements Iterator<double[]> { + private final double[][] array; + private int columnIndex = 0; + private int numColumns; + + public DoubleDoubleArrayColumnIterator(final double[][] array) { + Objects.requireNonNull(array); + this.array = array; + numColumns = Integer.MAX_VALUE; + for (double[] row: array) { + if (row.length < numColumns) numColumns = row.length; + } + if (numColumns == Integer.MAX_VALUE) numColumns = 0; + } + + @Override + public boolean hasNext() { + return columnIndex < numColumns; + } + + @Override + public double[] next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + double[] col = new double[array.length]; + for (int r = 0; r < array.length; r++) { + col[r] = array[r][columnIndex]; + } + columnIndex++; + return col; + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Remove not supported for arrays"); + } +} diff --git a/src/main/java/org/codehaus/groovy/util/FloatFloatArrayColumnIterator.java b/src/main/java/org/codehaus/groovy/util/FloatFloatArrayColumnIterator.java new file mode 100644 index 0000000000..62e343bd8a --- /dev/null +++ b/src/main/java/org/codehaus/groovy/util/FloatFloatArrayColumnIterator.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.groovy.util; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Objects; + +/** + * An iterator providing the columns of a float[][]. + * In the case of an array with different size rows, + * the number of columns will be equal to the length of the smallest row. + * + * @since 5.0.0 + */ +public class FloatFloatArrayColumnIterator implements Iterator<float[]> { + private final float[][] array; + private int columnIndex = 0; + private int numColumns; + + public FloatFloatArrayColumnIterator(final float[][] array) { + Objects.requireNonNull(array); + this.array = array; + numColumns = Integer.MAX_VALUE; + for (float[] row: array) { + if (row.length < numColumns) numColumns = row.length; + } + if (numColumns == Integer.MAX_VALUE) numColumns = 0; + } + + @Override + public boolean hasNext() { + return columnIndex < numColumns; + } + + @Override + public float[] next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + float[] col = new float[array.length]; + for (int r = 0; r < array.length; r++) { + col[r] = array[r][columnIndex]; + } + columnIndex++; + return col; + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Remove not supported for arrays"); + } +} diff --git a/src/main/java/org/codehaus/groovy/util/IntIntArrayColumnIterator.java b/src/main/java/org/codehaus/groovy/util/IntIntArrayColumnIterator.java new file mode 100644 index 0000000000..53e3de1a90 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/util/IntIntArrayColumnIterator.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.groovy.util; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Objects; + +/** + * An iterator providing the columns of an int[][]. + * In the case of an array with different size rows, + * the number of columns will be equal to the length of the smallest row. + * + * @since 5.0.0 + */ +public class IntIntArrayColumnIterator implements Iterator<int[]> { + private final int[][] array; + private int columnIndex = 0; + private int numColumns; + + public IntIntArrayColumnIterator(final int[][] array) { + Objects.requireNonNull(array); + this.array = array; + numColumns = Integer.MAX_VALUE; + for (int[] row: array) { + if (row.length < numColumns) numColumns = row.length; + } + if (numColumns == Integer.MAX_VALUE) numColumns = 0; + } + + @Override + public boolean hasNext() { + return columnIndex < numColumns; + } + + @Override + public int[] next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + int[] col = new int[array.length]; + for (int r = 0; r < array.length; r++) { + col[r] = array[r][columnIndex]; + } + columnIndex++; + return col; + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Remove not supported for arrays"); + } +} diff --git a/src/main/java/org/codehaus/groovy/util/LongLongArrayColumnIterator.java b/src/main/java/org/codehaus/groovy/util/LongLongArrayColumnIterator.java new file mode 100644 index 0000000000..3e9aa245e9 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/util/LongLongArrayColumnIterator.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.groovy.util; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Objects; + +/** + * An iterator providing the columns of a long[][]. + * In the case of an array with different size rows, + * the number of columns will be equal to the length of the smallest row. + * + * @since 5.0.0 + */ +public class LongLongArrayColumnIterator implements Iterator<long[]> { + private final long[][] array; + private int columnIndex = 0; + private int numColumns; + + public LongLongArrayColumnIterator(final long[][] array) { + Objects.requireNonNull(array); + this.array = array; + numColumns = Integer.MAX_VALUE; + for (long[] row: array) { + if (row.length < numColumns) numColumns = row.length; + } + if (numColumns == Integer.MAX_VALUE) numColumns = 0; + } + + @Override + public boolean hasNext() { + return columnIndex < numColumns; + } + + @Override + public long[] next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + long[] col = new long[array.length]; + for (int r = 0; r < array.length; r++) { + col[r] = array[r][columnIndex]; + } + columnIndex++; + return col; + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Remove not supported for arrays"); + } +}