Reputation: 23
I have an ArrayList. I want to group similar items, such that the letter.pdf is the first one in each group. For example:
123_Letter.pdf
123_Others.pdf
123_More.pdf
222_Second.pdf
222_Letter.pdf
222_Third.pdf
222_Fourth.pdf
123_File.pdf
The output should be:
**123_Letter.pdf**
123_Others.pdf
123_More.pdf
123_File.pdf
**222_Letter.pdf**
222_Second.pdf
222_Third.pdf
222_Fourth.pdf
The order of other elements in each group doesn't matter. There are more than 3000 elements in the list. As you can see, merely sorting is not of much help.
I tried something like this, but it is missing the last element 123_File.pdf. Any better way of doing this? Please help.
String root = list.get(0).substring(0,4);
ArrayList<String> al = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
while (list.get(i).substring(0, 4).equals(root)) {
if (list.get(i).endsWith("etter.pdf")) {
al.add(0, list.get(i));
i++;
} else {
al.add(list.get(i));
i++;
}
}
System.out.println(al);
al = new ArrayList<>();
root = list.get(i).substring(0, 4);
}
Upvotes: 0
Views: 2512
Reputation: 1717
It sounds like you want to sort according to two criteria:
One reasonable solution that's old-school is to implement a custom java.util.Comparator
, and use that as the basis of the sort:
public static List<String> sortOldSchool(List<String> list){
Comparator<String> comparator = new Comparator<String>(){
private static final String LETTER = "Letter.pdf";
public int compare(String orange, String apple){
String ostart = orange.substring(0,3);
String astart = apple.substring(0,3);
if (ostart.equals(astart)){
if (orange.endsWith(LETTER)){
return (apple.endsWith(LETTER)) ? 0 : -1;
}
else return (apple.endsWith(LETTER)) ? 1 : orange.compareTo(apple);
}
else return ostart.compareTo(astart);
}
};
Collections.sort(list, comparator);
return list;
}
A more modern approach would be to leverage the new functional paradigms available starting with Java 8:
public static List<String> sortFunctional(List<String> list){
Comparator<String> firstThree= Comparator.comparing(item -> item.substring(0,3));
Comparator<String> letter = Comparator.comparing(item -> (!item.endsWith("Letter.pdf")));
return list.stream()
.sorted(firstThree.thenComparing(letter))
.collect(Collectors.toList());
}
Upvotes: 3
Reputation: 2044
Based on what you have described - I would suggest breaking the problem into two sub-problems:
I have provided a code sample demonstrating each of the steps below:
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;
public class StackoverflowTest {
List<String> values = Arrays.asList(
"123_Letter.pdf",
"123_Others.pdf",
"123_More.pdf",
"222_Second.pdf",
"222_Letter.pdf",
"222_Third.pdf",
"222_Fourth.pdf",
"123_File.pdf");
List<String> expected = Arrays.asList(
"123_Letter.pdf",
"123_Others.pdf",
"123_More.pdf",
"123_File.pdf",
"222_Letter.pdf",
"222_Second.pdf",
"222_Third.pdf",
"222_Fourth.pdf"
);
@Test
public void sort() {
// Basic function to group the values by the prefix
Collector<String, ?, Map<String, List<String>>> groupByPrefix = Collectors.groupingBy(c -> c.substring(0, 3));
// Basic function to sort the groups with _Letter.pdf first
Function<List<String>, Stream<? extends String>> sortPrefixGroups = v ->
v.stream().sorted(Comparator.comparing(i -> !i.endsWith("_Letter.pdf")));
// Step 1: Organise the values into their prefix groups
Map<String, List<String>> groupedByPrefix = new TreeMap<>(values.stream().collect(groupByPrefix));
// Step 2: Sort each of the prefix groups and recombine them back into a list to maintain their internal order
List<String> collect = groupedByPrefix.entrySet().stream().map(Map.Entry::getValue).flatMap(sortPrefixGroups).collect(toList());
Assert.assertEquals(expected, collect);
//Print just for fun
collect.forEach(System.out::println);
}
}
Upvotes: 0
Reputation: 6528
How about using a HashMap
:
HashMap<String, List<String>> hashMap = new HashMap<String, List<String>>();
Now, iterate over the hashmap and add elements:
if (!hashMap.containsKey(listItem.substring(0,2)) {
List<String> items = new ArrayList<String>();
items.add(listItem);
hashMap.put(listItem.substring(0,2), items);
} else {
hashMap.get(listItem.substring(0,2)).add(items);
}
Upvotes: 0