Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

list-ops: add to track #207

Merged
merged 13 commits into from
Feb 21, 2017
Empty file added .keep
Empty file.
5 changes: 5 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,11 @@
"slug": "change",
"difficulty": 1,
"topics": []
},
{
"slug": "list-ops",
"difficulty": 1,
"topics": []
}
],
"deprecated": [
Expand Down
18 changes: 18 additions & 0 deletions exercises/list-ops/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apply plugin: "java"
apply plugin: "eclipse"
apply plugin: "idea"

repositories {
mavenCentral()
}

dependencies {
testCompile "junit:junit:4.12"
}

test {
testLogging {
exceptionFormat = 'full'
events = ["passed", "failed", "skipped"]
}
}
51 changes: 51 additions & 0 deletions exercises/list-ops/src/example/java/ListOps.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class ListOps {

private ListOps() {
}

public static <T> int length(final List<T> list) {
return list.size();
}

public static <T> List<T> reverse(final List<T> list) {
List<T> result = new ArrayList(list);
Collections.reverse(result);
return result;
}

public static <T> List<T> map(final List<T> list,
UnaryOperator<T> mapper) {
return list.stream().map(mapper).collect(Collectors.toList());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hahaha!! Cheater! 😆

👍
I joke, but actually, relying on the JSL for this functionality gives me great confidence that the tests are solid. Not sure if that was your intention, but great move!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried to keep it simple. The example is needed only to test the test. No pun intended. :)

}

public static <T> List<T> filter(final List<T> list,
Predicate<T> predicate) {
return list.stream().filter(predicate).collect(Collectors.toList());
}

public static <U, T> U reduce(final List<T> list,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍
I love how this mirrors exactly the interface for streams... what a great way to introduce this part of the API!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. It makes sense to expose Java API here like the C# one was used in the C# track

U identity,
BiFunction<U, T, U> accumulator,
BinaryOperator<U> combiner) {
return list.stream().reduce(identity, accumulator, combiner);
}

public static <T> List<T> concat(final List<T>... lists) {
return Stream.of(lists)
.flatMap(Collection::stream)
.collect(Collectors.toList());
}

}
Empty file.
316 changes: 316 additions & 0 deletions exercises/list-ops/src/test/java/ListOpsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,316 @@

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertTrue;
import org.junit.Ignore;
import org.junit.Test;

public class ListOpsTest {

private static final List<Integer> EMPTY_LIST
= Collections.emptyList();

@Test
public void lengthOfAnEmptyListShouldBeZero() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here (and in mapOfAnEmptyListShouldBeAnEmptyList(), fileteredEmptyListShouldBeAnEmptyList()), the subject becomes a property of the list whereas in the other tests, the subject is the list itself.

🤔
Genuine question: is it "better" to have consistent test method naming? or to have a variety to break up the monotony that comes with consistency??! :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed. Just failed to come up with consistent names on the first pass. :)

final int expected = 0;
final int actual = ListOps.length(EMPTY_LIST);

assertEquals(expected, actual);
}

@Test
@Ignore
public void shouldReturnTheCorrectLengthOfAnNonEmptyList() {
final List<Integer> list = Collections.unmodifiableList(
Arrays.asList(0, 1, 2, 3, 4)
);
final int actual = ListOps.length(list);
final int expected = list.size();

assertEquals(expected, actual);
}

@Test
@Ignore
public void shouldReverseAnEmptyList() {
final List<Integer> actual = ListOps.reverse(EMPTY_LIST);

assertNotNull(actual);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍
I've said it before, but I really appreciate how little bits like this smooth-out the TDD experience: the more we keep folks in test failures (rather than test errors), the smoother the experience.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. :)

assertTrue(actual.isEmpty());
}

@Test
@Ignore
public void shouldReverseANonEmptyList() {
final List<Integer> list = Collections.unmodifiableList(
Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8)
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

final List<Integer> actual
= ListOps.reverse(list);
final List<Integer> expected
= Arrays.asList(8, 7, 6, 5, 4, 3, 2, 1, 0);

assertNotNull(actual);
assertFalse(actual.isEmpty());
assertEquals(expected, actual);
}

@Test
@Ignore
public void shouldMapAnEmptyListAndReturnAnEmptyList() {
final List<Integer> actual = ListOps.map(EMPTY_LIST, x -> x + 1);

assertNotNull(actual);
assertTrue(actual.isEmpty());
}

@Test
@Ignore
public void shouldMapNonEmptyList() {
final List<Integer> list
= Collections.unmodifiableList(Arrays.asList(1, 3, 5, 7));
final List<Integer> actual = ListOps.map(list, x -> x + 1);

assertNotNull(actual);
assertFalse(actual.isEmpty());
assertEquals(Arrays.asList(2, 4, 6, 8), actual);
}

@Test
@Ignore
public void shouldFilterAnEmptyListanddReturnAnEmptyList() {
final List<Integer> actual = ListOps.filter(EMPTY_LIST, x -> x > 0);

assertNotNull(actual);
assertTrue(actual.isEmpty());
}

@Test
@Ignore
public void shouldFilterNonEmptyList() {
Predicate<Integer> predicate = x -> x % 2 > 0;
final List<Integer> list = Collections.unmodifiableList(
IntStream.range(0, 100).boxed().collect(Collectors.toList())
);
final List<Integer> actual = ListOps.filter(list, predicate);
final List<Integer> expected = list.stream()
.filter(predicate)
.collect(Collectors.toList());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 I think I had suggested in a previous review that you eliminate the need to calculate expected by making the expected result much smaller (and therefore easy to express literally). However, I see now that this is a great opportunity to highlight the Java Stream API. I dig it.


assertNotNull(actual);
assertFalse(actual.isEmpty());
assertEquals(expected, actual);
}

@Test
@Ignore
public void shouldConcatenateZeroLists() {
List<Integer> actual = ListOps.concat();

assertNotNull(actual);
assertTrue(actual.isEmpty());
}

@Test
@Ignore
public void shouldConcatenateOneNonEmptyList() {
final List<Integer> list
= Collections.unmodifiableList(
Arrays.asList(0, 1, 2, 3, 4)
);
final List<Integer> actual = ListOps.concat(list);
final List<Integer> expected = Arrays.asList(0, 1, 2, 3, 4);

assertNotNull(actual);
assertFalse(actual.isEmpty());
assertEquals(expected, actual);
}

@Test
@Ignore
public void shouldConcatenateOneEmptyList() {
final List<Integer> actual = ListOps.concat(EMPTY_LIST);

assertNotNull(actual);
assertTrue(actual.isEmpty());
}

@Test
@Ignore
public void shouldConcatenateTwoEmptyLists() {
final List<Integer> actual = ListOps.concat(EMPTY_LIST, EMPTY_LIST);

assertNotNull(actual);
assertTrue(actual.isEmpty());
}

@Test
@Ignore
public void shouldConcatenateOneEmptyAndOneNonEmptyLists() {
final List<Integer> list
= Collections.unmodifiableList(
Arrays.asList(0, 1, 2, 3, 4)
);
final List<Integer> actual = ListOps.concat(list, EMPTY_LIST);
final List<Integer> expected
= Arrays.asList(0, 1, 2, 3, 4);

assertNotNull(actual);
assertFalse(actual.isEmpty());
assertEquals(expected, actual);
}

@Test
@Ignore
public void shouldConcatenateOneNonEmptyAndOneEmptyLists() {
final List<Integer> list
= Collections.unmodifiableList(
Arrays.asList(0, 1, 2, 3, 4)
);
final List<Integer> actual = ListOps.concat(EMPTY_LIST, list);
final List<Integer> expected
= Arrays.asList(0, 1, 2, 3, 4);

assertNotNull(actual);
assertFalse(actual.isEmpty());
assertEquals(expected, actual);
}

@Test
@Ignore
public void shouldConcatenateTwoListsWithSameElements() {
final List<Integer> list1 = Collections.unmodifiableList(
Arrays.asList(0, 1, 2, 3, 4)
);
final List<Integer> list2 = Collections.unmodifiableList(
Arrays.asList(1, 2, 3, 4, 5, 6)
);
final List<Integer> expected
= Arrays.asList(0, 1, 2, 3, 4, 1, 2, 3, 4, 5, 6);
final List<Integer> actual = ListOps.concat(list1, list2);

assertNotNull(actual);
assertFalse(actual.isEmpty());
assertEquals(expected, actual);
}

@Test
@Ignore
public void shouldConcatenateSeveralLists() {
final List<Integer> list1 = Collections.unmodifiableList(
Arrays.asList(0, 1, 2, 3)
);
final List<Integer> list2 = Collections.unmodifiableList(
Arrays.asList(4, 5, 6, 7)
);
final List<Integer> list3 = Collections.unmodifiableList(
Arrays.asList(8, 9, 10, 11)
);
final List<Integer> list4 = Collections.unmodifiableList(
Arrays.asList(12, 13, 14, 15)
);
final List<Integer> expected
= Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
14, 15);

final List<Integer> actual
= ListOps.concat(list1, list2, EMPTY_LIST, list3, list4);

assertNotNull(actual);
assertFalse(actual.isEmpty());
assertEquals(expected, actual);
}

@Test
@Ignore
public void shouldReturnIdentityWhenAnEmptyListIsReduced() {
final int expected = 0;
final int actual
= ListOps.reduce(EMPTY_LIST, 0, (x, y) -> x + y, Integer::sum);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍
Nice touch! I love how you're seeking in little bits of the Java FP stuff in! (Here, both the method reference and that there's a set of functions ready for use!)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. :)


assertEquals(expected, actual);
}

@Test
@Ignore
public void shouldCalculateTheSumOfANonEmptyIntegerList() {
final List<Integer> list = Collections.unmodifiableList(
Arrays.asList(0, 1, 2, 3, 4)
);
final int actual = ListOps.reduce(list, 0,
(x, y) -> x + y,
Integer::sum);

assertEquals(10, actual);
}

/*
https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#reduce-U-java.util.function.BiFunction-java.util.function.BinaryOperator-
*/
private BiFunction<List<Integer>, Integer, List<Integer>> accumulator
= (List<Integer> partial, Integer elem) -> {
List<Integer> result = new ArrayList<>(partial);
result.add(elem);
return result;
};

private BinaryOperator<List<Integer>> combiner
= (list1, list2) -> {
List<Integer> result = new ArrayList<>(list1);
result.addAll(list2);
return result;
};

@Test
@Ignore
public void shouldReduceAnEmptyListAndANonEmptyListAndReturnConcatenation() {
final List<Integer> list = Collections.unmodifiableList(
Arrays.asList(0, 1, 2, 3, 4, 5)
);
final List<Integer> actual
= ListOps.reduce(list,
new ArrayList<Integer>(),
accumulator,
combiner);
final List<Integer> expected
= Arrays.asList(0, 1, 2, 3, 4, 5);

assertNotNull(actual);
assertFalse(actual.isEmpty());
assertEquals(expected, actual);
}

@Test
@Ignore
public void shouldReduceTwoNonEmptyListsAndReturnConcatenation() {
final List<Integer> listOne = Collections.unmodifiableList(
Arrays.asList(0, 1, 2, 3, 4)
);
final List<Integer> listTwo = Collections.unmodifiableList(
Arrays.asList(5, 6, 7, 8, 9)
);
final List<Integer> actual
= ListOps.reduce(listTwo,
listOne,
accumulator,
combiner);
final List<Integer> expected
= Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);

assertNotNull(actual);
assertFalse(actual.isEmpty());
assertEquals(expected, actual);
}

}
Loading