I love lambdaj and use it a lot, but I can't seem to figure out if it is possible to sort a list using multiple sort conditions.
Here is an example using Google Collections. Can the same thing be done in lambdaj?
Sorted first by color then by name:
Function<Fruit, Color> getColorFunction = new Function<Fruit, Color>() {
public Color apply(Fruit from) {
return from.getColor();
}
};
Function<Fruit, String> getNameFunction = new Function<Fruit, String>() {
public String apply(Fruit from) {
return from.getName();
}
};
Ordering<Fruit> colorOrdering = Ordering.natural().onResultOf(getColorFunction);
Ordering<Fruit> nameOrdering = Ordering.natural().onResultOf(getNameFunction);
Ordering<Fruit> colorAndNameOrdering = colorOrdering.compound(nameOrdering);
ImmutableSortedSet<Fruit> sortedFruits = ImmutableSortedSet.orderedBy(
colorAndNameOrdering).addAll(fruits).build();
Examples of doing that aren't provided on official Lambdaj page, but under hood each time when you are calling sort Lambdaj creates comparator for given argument. Below is an example how to sort by multiple attributes.
Sorting:
public static void main(String... args) {
final Comparator byName = new ArgumentComparator(on(Fruit.class).getName());
final Comparator byColor = new ArgumentComparator(on(Fruit.class).getColor());
final Comparator orderBy = ComparatorUtils.chainedComparator(byName, byColor);
final List<Fruit> unsorted = Arrays.asList(...);
final List<Fruit> sorted = sort(unsorted, on(Fruit.class), orderBy);
System.out.println(sorted);
}
Downside:
There's one non-lambdaj 3rd party method usage. ComparatorUtils.chainedComparator - it's from apache commons collections. If you don't have this as a dependency in your project you can write your own. it just iterates thru all comparators untill non zero returned.
Related
I am wondering how to build a nested Map and then retrieve the nested key:value pairs. This is how I created the map to be nested.
//Create List of Nested HashMaps
List<Map> nestedMap = new ArrayList<>();
nestedMap.add(building3Map);
nestedMap.add(building31Map);
nestedMap.add(buildingHFTFMap);
System.out.println("The nested map is: " + nestedMap);
This is the system output for the following code:
The nested map is: [{buildingname=[Building 3], buildingid=[3]}, {buildingname=[Building 31], buildingid=[1]}, {buildingname=[HFTF], buildingid=[4]}]
This is correct as I want a list of maps. But the next step is what is confusing to me. When I try to build the outer layer Map as below:
HashMap<String, List<Map>> queryMap = new HashMap<>();
queryMap.put("buildings", nestedMap);
System.out.println(queryMap.get("buildings.buildingid"));
I get a system output of null when attempting the .get("buildings.buildingid") method. Ideally, I need the output to look like this:
[[3, 1, 4]]
Where it returns all values with a key of "buildings.buildingid" in an array. I am new to coding so please let me know if I'm making any fundamental errors and how I can create a nested Map where I can access the inner layer or if there is another method I should be using.
I think you are making it way too complicated than it should be. you can store your data in a simple map where the ids are the keys for example and the names are the values. So you only need to read the keys or the values from the map to get your result.
Map<Integer, String> myMap = new HashMap<>();
myMap.put(3, "Building 3");
myMap.put(31, "Building 31");
myMap.put(4, "HFTF");
System.out.println(myMap.keySet());
System.out.println(myMap.values());
However, Java is an object-oriented language. If it makes sense for your use case you might want to create a custom class "Building" instead of frantically trying to store your data in nested data structures. See below for an example of how it might look using a custom class:
import java.util.ArrayList;
import java.util.List;
public class Example {
public static void main(String args[]) {
List<Building> buildings = new ArrayList<>();
buildings.add(new Building(3, "Building 3"));
buildings.add(new Building(31, "Building 31"));
buildings.add(new Building(4, "HFTF"));
List<Integer> buildingIds = new ArrayList<>();
buildings.forEach(building -> buildingIds.add(building.getBuildingId()));
List<String> buildingNames = new ArrayList<>();
buildings.forEach(building -> buildingNames.add(building.getBuildingName()));
System.out.println(buildingIds);
System.out.println(buildingNames);
}
public static class Building {
int buildingId;
String buildingName;
public Building(final int buildingId, final String buildingName) {
this.buildingId = buildingId;
this.buildingName = buildingName;
}
public int getBuildingId() {
return buildingId;
}
public void setBuildingId(final int buildingId) {
this.buildingId = buildingId;
}
public String getBuildingName() {
return buildingName;
}
public void setBuildingName(final String buildingName) {
this.buildingName = buildingName;
}
}
}
queryMap.get("buildings.buildingid") returns null, because queryMap only contains a value under the key
buildings. A HashMap can only access the value using the same key it was stored under. The key and the value is not processed in any further way.
A simple alternative could be
queryMap.get("buildings").stream() // Create a Stream<Map>
.map(building -> building.get("buildingid")) // Create Stream<String> by extracting the buildingid values.
.collect(Collectors.toList()); // Collect Stream into a List<String> which contains the buildingid's
If you don't like this solution you could take a deeper look into property expressions, and write your own map implementation that can resolve these expressions. But it will be a lot of work to get it working correctly...
Can you group List<TypeEnum> types = new ArrayList(Arrays.asList(TypeEnum.A, TypeEnum.B, TypeEnum.A)); into a Map<TypeEnum, Integer> countPerType;, using functors (e.g. Google's Guava, Apache's Commons Functor) pre Java 8?
I'm trying to get my head around functional programming, but am unsure if that sort of thing would actually be possible (as I'm not just mapping a collections value, but trying to aggregate)?
In imperative style, I would have done something like this:
public Map<TypeEnum, Integer> countByGroup(List<TypeEnum> types) {
Map<TypeEnum, Integer> countedTypes = new HashMap<>();
for(TypeEnum type : types) {
if(countedTypes.containsKey(type)) {
countedTypes.put(type, countedTypes.get(type) + 1);
} else {
countedTypes.put(type, 1);
}
}
return countedTypes;
}
EDIT: Relying on side effects seems somewhat inappropriate - or is that how it's done...?
Procedure<TypeEnum> count = new Procedure<TypeEnum>() {
public Map<TypeEnum, Integer> countPerType = null;
#Override
public void run(TypeEnum type) {
if(countPerType.containsKey(type)) {
countPerType.put(type, countPerType.get(type) + 1);
} else {
countPerType.put(type, 1);
}
}
public Procedure<TypeEnum> init(Map<TypeEnum, Integer> countPerType) {
this.countPerType = countPerType;
return this;
}
}.init(countPerType); // kudos http://stackoverflow.com/a/12206542/2018047
Olivier Grégoire commented the correct answer:
Using Guava, you want a simple Multiset, and more specifically its EnumMultiset implementation. Multiset is a data structure made to keep track of counted elements.
Given your List<TypeEnum> types you can create an EnumMultiset using create:
Multiset<TypeEnum> multiset = EnumMultiset.create(types);
And you can query for the count of an element in the Multiset using count:
multiset.count(TypeEnum.A); // 2
multiset.count(TypeEnum.B); // 1
How can I iterate over list of POJO classes for collecting result of some methods in a standard way to avoid copy past?
I want to have code like this:
//class 'Person' has methods: getNames(), getEmails()
List<Person> people = requester.getPeople(u.getId());
String names = merge(people, Person::getNames);
String emails = merge(people, Person::getEmails);
instead of such copy-pasted logic:
List<Person> people = requester.getPeople(u.getId());
Set<String> namesAll = new HashSet<>();
Set<String> emailsAll = new HashSet<>();
for (Person p : people) {
if(p.getNames()!=null) {
phonesAll.addAll(p.getNames());
}
if(p.getEmails()!=null) {
emailsAll.addAll(p.getEmails());
}
}
String names = Joiner.on(", ").skipNulls().join(namesAll);
String emails = Joiner.on(", ").skipNulls().join(emailsAll);
Thus, is it possible to implement some standard approach for iterating and processing special method of POJO in list that could be reused?
If I understand you correctly, you want something like this :
String names = people.stream().flatMap(p->p.getNames().stream()).distinct().collect(Collectors.joining(", "));
Now, if you want to save typing that line for each property, you can have this merge method as you suggested :
public static String merge (List<Person> people, Function<Person, Collection<String>> mapper)
{
return people.stream().flatMap(p->mapper.apply(p).stream()).distinct().collect(Collectors.joining(", "));
}
This would make your first snippet work.
Now, you can make this method generic :
public static <T> String merge (List<T> list, Function<T, Collection<String>> mapper)
{
return list.stream().flatMap(p->mapper.apply(p).stream()).distinct().collect(Collectors.joining(", "));
}
I think this should work (haven't tested it).
I have two maps:
Map<Date, List<Journey>> journeyMap = new TreeMap<Date, List<Journey>>
Map<Date, List<Job>> jobMap = new TreeMap<Date, List<Job>>
I used TreeMap because that means they're sorted by date but I want to go through both maps at the same time, get the values of Journey/Job, then do some work.
I think i could use generics, storing the Job/Journey as an Object, then checking the instanceOf but I'm not sure if thats the solution?
Thanks.
Even though the others are right, that there are better, safer and more comfortable ways to achive whatever you want, it is possible to iterate over (the entries of) two Maps (aka Collections) at the same time.
//replace keySet() with your favorite method in for-each-loops
Iterator<Date> journeyIterator = journeyMap.keySet().iterator()
Iterator<Date> jobIterator = jobMap.keySet().iterator();
while(journeyIterator.hasNext() && jobIterator.hasNext()){
Date journeyDate = journeyIter.next()
Date jobDate = jobIterator.next();
//... do whatever you want with the data
}
This code does explicitly, what a for-each-loop can do implicitly for one Collection. It retrieves the Iterator and gets the element from the Collection from it, much like reading a file.
You're making an assumption that these maps are having values sorted in the very same way, but this is definitely not correct. At least if you want to write a logic like this you need to declare the same implementing class as a reference:
TreeMap<Date, List<Journey>> journeyMap = new TreeMap<Date, List<Journey>>
TreeMap<Date, List<Job>> jobMap = new TreeMap<Date, List<Job>>
but believe me you don't want to do it.
You're right! Instead doing 2 maps create 1, holding pair of Job/Journey objects - create a JobJourneyHolder class which holds both objects, this will be a good solution.
Yes, defining a new class for that is definitely the solution, because it composes related objects together, which is very welcomed in OOP. And you should not forget to implement hashCode() and equals() methods to make such classes work properly in Java collections:
public final class JourneyJob {
final Journey journey;
final Job job;
public JourneyJob(Journey journey, Job job) {
if (journey == null || job == null)
throw new NullPointerException();
this.journey = journey;
this.job = job;
}
public int hashCode() {
return Objects.hash(journey, job);
}
public boolean equals(JourneyJob other) {
return other.job.equals(job) && other.journey.equals(journey);
}
}
To add elements to common Map:
Map<Date, List<JourneyJob>> map = new TreeMap<>();
...
if (map.contains(date)) {
map.get(date).add(new JourneyJob(journey, job));
} else {
map.put(date, new ArrayList<>(Arrays.asList(new JourneyJob(journey, job)));
}
...
To retrieve JourneyJob objects:
for (List<JourneyJob> jjList : map.values()) {
for (JourneyJob jj : jjList) {
journey = jj.journey;
job = jj.job;
//... do your work here
}
}
Or, if you use Java 8, this can be done using nested forEach():
map.values().stream().forEach(list ->
list.stream().forEach(jj -> {
Journey journey = jj.journey;
Job job = jj.job;
//... do your work here
})
);
Since Java doesn't allow passing methods as parameters, what trick do you use to implement Python like list comprehension in Java ?
I have a list (ArrayList) of Strings. I need to transform each element by using a function so that I get another list. I have several functions which take a String as input and return another String as output. How do I make a generic method which can be given the list and the function as parameters so that I can get a list back with each element processed. It is not possible in the literal sense, but what trick should I use ?
The other option is to write a new function for each smaller String-processing function which simply loops over the entire list, which is kinda not so cool.
In Java 8 you can use method references:
List<String> list = ...;
list.replaceAll(String::toUpperCase);
Or, if you want to create a new list instance:
List<String> upper = list.stream().map(String::toUpperCase).collect(Collectors.toList());
Basically, you create a Function interface:
public interface Func<In, Out> {
public Out apply(In in);
}
and then pass in an anonymous subclass to your method.
Your method could either apply the function to each element in-place:
public static <T> void applyToListInPlace(List<T> list, Func<T, T> f) {
ListIterator<T> itr = list.listIterator();
while (itr.hasNext()) {
T output = f.apply(itr.next());
itr.set(output);
}
}
// ...
List<String> myList = ...;
applyToListInPlace(myList, new Func<String, String>() {
public String apply(String in) {
return in.toLowerCase();
}
});
or create a new List (basically creating a mapping from the input list to the output list):
public static <In, Out> List<Out> map(List<In> in, Func<In, Out> f) {
List<Out> out = new ArrayList<Out>(in.size());
for (In inObj : in) {
out.add(f.apply(inObj));
}
return out;
}
// ...
List<String> myList = ...;
List<String> lowerCased = map(myList, new Func<String, String>() {
public String apply(String in) {
return in.toLowerCase();
}
});
Which one is preferable depends on your use case. If your list is extremely large, the in-place solution may be the only viable one; if you wish to apply many different functions to the same original list to make many derivative lists, you will want the map version.
The Google Collections library has lots of classes for working with collections and iterators at a much higher level than plain Java supports, and in a functional manner (filter, map, fold, etc.). It defines Function and Predicate interfaces and methods that use them to process collections so that you don't have to. It also has convenience functions that make dealing with Java generics less arduous.
I also use Hamcrest** for filtering collections.
The two libraries are easy to combine with adapter classes.
** Declaration of interest: I co-wrote Hamcrest
Apache Commons CollectionsUtil.transform(Collection, Transformer) is another option.
I'm building this project to write list comprehension in Java, now is a proof of concept in https://github.com/farolfo/list-comprehension-in-java
Examples
// { x | x E {1,2,3,4} ^ x is even }
// gives {2,4}
Predicate<Integer> even = x -> x % 2 == 0;
List<Integer> evens = new ListComprehension<Integer>()
.suchThat(x -> {
x.belongsTo(Arrays.asList(1, 2, 3, 4));
x.is(even);
});
// evens = {2,4};
And if we want to transform the output expression in some way like
// { x * 2 | x E {1,2,3,4} ^ x is even }
// gives {4,8}
List<Integer> duplicated = new ListComprehension<Integer>()
.giveMeAll((Integer x) -> x * 2)
.suchThat(x -> {
x.belongsTo(Arrays.asList(1, 2, 3, 4));
x.is(even);
});
// duplicated = {4,8}
You can use lambdas for the function, like so:
class Comprehension<T> {
/**
*in: List int
*func: Function to do to each entry
*/
public List<T> comp(List<T> in, Function<T, T> func) {
List<T> out = new ArrayList<T>();
for(T o: in) {
out.add(func.apply(o));
}
return out;
}
}
the usage:
List<String> stuff = new ArrayList<String>();
stuff.add("a");
stuff.add("b");
stuff.add("c");
stuff.add("d");
stuff.add("cheese");
List<String> newStuff = new Comprehension<String>().comp(stuff, (a) -> { //The <String> tells the comprehension to return an ArrayList<String>
a.equals("a")? "1":
(a.equals("b")? "2":
(a.equals("c")? "3":
(a.equals("d")? "4": a
)))
});
will return:
["1", "2", "3", "4", "cheese"]
import java.util.Arrays;
class Soft{
public static void main(String[] args){
int[] nums=range(9, 12);
System.out.println(Arrays.toString(nums));
}
static int[] range(int low, int high){
int[] a=new int[high-low];
for(int i=0,j=low;i<high-low;i++,j++){
a[i]=j;
}
return a;
}
}
Hope, that I help you :)