How to merge a few lists? - java

How to combine three lists:
List<String> one = Arrays.asList("one","four","seven");
List<String> two = Arrays.asList("two","five","eight");
List<String> three = Arrays.asList("three","six");
List<List<String>> merged = ...;
To get in result this:
List {one, two, three, four, five , six , seven, eight}
In real case I need get some queue of matches, from lists (groups) of participants. That in first match play all first participants from different groups. In second match play all second participants and so on. And I will not be able to sort it out.

You can use Java 8 streams to easily merge your lists together and flatMap() it to a single List of String. Sorting by "one", "two", "three" etc. will require a custom sorter because simply sorting by String.compareTo() or any other natural sort will do lexical comparison of characters, not their meanings in English.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Merge {
public static void main(String[] args) {
List<String> one = Arrays.asList("one","four","seven");
List<String> two = Arrays.asList("two","five","eight");
List<String> three = Arrays.asList("three","six");
List<List<String>> merged = Arrays.asList(one, two, three);
System.out.println(merged);
// Output -> [[one, four, seven], [two, five, eight], [three, six]]
List<String> flatMapped = merged.stream().flatMap(List::stream).collect(Collectors.toList());
System.out.println(flatMapped);
// Output -> [one, four, seven, two, five, eight, three, six]
flatMapped.sort((o1, o2) -> {
// TODO - implement sorting logic to return "one", "two", "three", etc.
});
}
}

Two points:
- You cannot sort number words, written in Strings by the number. Therefore you have to do some mapping, e.g. with a Map, which is then sort by keys or you write a Comparator for the sort method
- Arrays.asList(...) generates a unmodifiable list, you cannot add something to it
So, this works for me:
List<String> one = new ArrayList<>(Arrays.asList("one", "four", "seven"));
List<String> two = new ArrayList<>(Arrays.asList("two", "five", "eight"));
List<String> three = new ArrayList<>(Arrays.asList("three", "six", "ten"));
one.addAll(two);
one.addAll(three);
one.sort((o1, o2) -> {
// Declare here, which value is less, equal or greater than the other
return 0;
});

By default, when you sort a list of String's, they will be sorted in lexicographic order (a-z, 0-9, etc.), and thus you cannot expect Collections.sort to sort your list of String's in the order that you expect.
Thus you will need to implement a custom Comparator that can do what want, and use the method signature Collections.sort(list, yourCustomComparator).

If you want just to combine the lists without altering their order, then you can do something like:
public static void main(String[] args) {
List<String> one = Arrays.asList("one","four","seven");
List<String> two = Arrays.asList("two","five","eight");
List<String> three = Arrays.asList("three","six");
System.out.println(zipLists(one, two, three));
//[one, two, three, four, five, six, seven, eight]
}
public static List<String> zipLists(List<String>... lists) {
int maxSize = 0, totalSize = 0;
List<Iterator<String>> iterators = new ArrayList<>(lists.length);
for(List<String> list: lists) {
int size = list.size();
maxSize = Math.max(maxSize, size);
totalSize += size;
iterators.add(list.iterator());
}
List<String> mergedList = new ArrayList<>(totalSize);
for(int i = 0; i < maxSize; i++) {
for(Iterator<String> iterator: iterators) {
if(iterator.hasNext()) {
mergedList.add(iterator.next());
}
}
}
return mergedList;
}

Assuming all you want to do is take elements from the source lists one at a time and 'weave' them in with the other lists...
Recursive solution:
public static List<String> MergeLists (List<List<String>> a2 ) {
List<String> h = new ArrayList<String>();
List<List<String>> a = new ArrayList<List<String>>();
boolean call = false;
for (List<String> l : a2) {
if (! l.isEmpty()) {
h.add(l.get(0));
if (l.size() > 0) {
a.add((List<String>) l.subList(1, l.size()));
call = true;
}
}
}
if (call) { h.addAll(MergeLists(a)); return h;}
else return h;
}

Related

Split a List of Strings based on value in Java

What I want to do is to split an array of strings, when the first 6 characters in the string are zeroes ("000000") or when all the digits in the string are zeroes. Limiting to 6 characters won't be very dynamic.
I got this code, and it does what I want to achieve.
import java.util.*;
public class Main
{
public static void main(String[] args) {
ArrayList<String> unsplitted = new ArrayList<String>();
unsplitted.add("000000: this_should_go_into_first_array");
unsplitted.add("000234: something1");
unsplitted.add("0000ff: something2");
unsplitted.add("000111: something3");
unsplitted.add("000051: something4");
unsplitted.add("007543: something5");
unsplitted.add("000000: and_this_should_go_into_second_array");
unsplitted.add("005612: something7");
unsplitted.add("005712: something8");
System.out.println("Unsplitted list: "+ unsplitted);
List<String> arrlist1 = unsplitted.subList(0, 6);
List<String> arrlist2 = unsplitted.subList(6, unsplitted.size());
System.out.println("Sublist of arrlist1: "+ arrlist1);
System.out.println("Sublist of arrlist2: "+ arrlist2);
}
}
Which prints out the wanted results
Sublist of arrlist1: [000000: this_should_go_into_first_array, 000234: something1, 0000ff: something2, 000111: something3, 000051: something4, 007543: somethi
ng5]
Sublist of arrlist2: [000000: and_this_should_go_into_second_array, 005612: something7, 005712: something8]
However, I don't know the indexes for the zeroes beforehand, so how can I achieve the same result by finding the zeroes dynamically?
You can simply iterate in your array and create "bucket" each time you detect your 000000 string :
ArrayList<String> unsplitted = new ArrayList<String>();
unsplitted.add("000000: this_should_go_into_first_array");
unsplitted.add("000234: something1");
unsplitted.add("0000ff: something2");
unsplitted.add("000111: something3");
unsplitted.add("000051: something4");
unsplitted.add("007543: something5");
unsplitted.add("000000: and_this_should_go_into_second_array");
unsplitted.add("005612: something7");
unsplitted.add("005712: something8");
List<List<String>> results = new ArrayList<>();
unsplitted.forEach(w -> {
if(w.startsWith("000000") || results.isEmpty()) {
// no bucket or detect 000000
List<String> bucket = new ArrayList<>();
bucket.add(w);
results.add(bucket);
}
else {
// not contains 00000 put the value in the last bucket
results.get(results.size() - 1).add(w);
}
});
results.forEach(w -> {
System.out.println("Sublist " + w);
});
Is it the result that you expected ?
The result :
Sublist [000000: this_should_go_into_first_array, 000234: something1, 0000ff: something2, 000111: something3, 000051: something4, 007543: something5]
Sublist [000000: and_this_should_go_into_second_array, 005612: something7, 005712: something8]
The question is quite interesting. There are different way to implement this, but I am going to show you a solution where it can be applied with any length of the first part, which we can consider as a key.
As you said in your introduction, it wouldn't be dynamic if the check was limited to only 6 characters. Based on this, as an example, you can take the position of the character ':' as reference and apply a partitioning among the elements of the array.
Here is the solution I propose:
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
public class Main
{
public static void main(String[] args) {
ArrayList<String> unsplitted = new ArrayList<String>();
unsplitted.add("000000: this_should_go_into_first_array");
unsplitted.add("000234: something1");
unsplitted.add("0000ff: something2");
unsplitted.add("000111: something3");
unsplitted.add("000051: something4");
unsplitted.add("007543: something5");
unsplitted.add("000000: and_this_should_go_into_second_array");
unsplitted.add("005612: something7");
unsplitted.add("005712: something8");
System.out.println("Non-split list: "+ unsplitted);
Predicate<String> filter = (String s) -> {
int indexOfCol = s.indexOf(":");
return s.substring(0, indexOfCol).equals("0".repeat(indexOfCol));
};
Map<Boolean, List<String>> splitMap = unsplitted.stream()
.collect(Collectors.partitioningBy(filter));
List<String> arrayZeroStart = splitMap.get(true);
List<String> arrayNonZeroStart = splitMap.get(false);
System.out.println("Sublist of arrayZeroStart: "+ arrayZeroStart);
System.out.println("Sublist of arrayWithout: "+ arrayNonZeroStart);
}
}
And here is the output:
Non-split list: [000000: this_should_go_into_first_array, 000234: something1, 0000ff:
something2, 000111: something3, 000051: something4, 007543: something5, 000000: and_this_should_go_into_second_array, 005612: something7, 005712: something8]
Sublist of arrayZeroStart: [000000: this_should_go_into_first_array, 000000: and_this_should_go_into_second_array]
Sublist of arrayWithout: [000234: something1, 0000ff: something2, 000111: something3, 000051: something4, 007543: something5, 005612: something7, 005712: something8]

Find non-similar elements from 2 array list having different sizes in Java

I have two array lists of string type as:
List a -> [Mango, Banana, Apple]
List b -> [Man, Apple]
I have to find out non-similar elements from two lists.
Till I have implemented this:
List d = new ArrayList(a);
toReturn.removeAll(b);
return d;
But the problem with this code is that I don't want Mango as the first element from list b contains "Man" string. I only want "Banana" to be returned.
You could iterate over one list and find the items in it that aren't substrings of the other, and then of course to the same with the arguments reversed:
private static Stream<List> filterNonSimilar(List<String> a, List<String> b) {
return a.stream()
.filter(ai -> b.stream().noneMatch(bi -> ai.contains(bi) || bi.contains(ai));
}
public static List<String> nonSimilar(List<String> a, List<String> b) {
return Stream.concat(filterNonSimilar(a, b), filterNonSimilar(b, a))
.collect(Collectors.toList());
}
You have iterate one list and then have to compare it in another, remove element if that exist in second list (viceversa), below I m sharing sample code for this approach.
public class Comp {
public static void main(String... strings) {
List<String> lis1 = new ArrayList<>();
lis1.add("applefy");
lis1.add("boy");
lis1.add("carrr");
List<String> lis2 = new ArrayList<>();
lis2.add("apple");
lis2.add("car");
List<String> result = new ArrayList<>();
for (String a : lis1) {
for (String b : lis2) {
if (a.contains(b)) {
result.add(a);
}
}
}
lis1.removeAll(result);
System.out.println(lis1);
}
}
output : [boy]
Hope this will help.

Moving several items in an ArrayList

I have the following data in an ArrayList. Let's say it's a String ArrayList for convenience sake.
Mokey
MokeyBaby1
MokeyBaby2
MokeyBaby3
Dog
DogBaby1
DogBaby2
Cat
CatBaby1
I need to move the items that are related, together.
For example: Moving Monkey down. The new ArrayList would look like this.
Dog
DogBaby1
DogBaby2
Mokey
MokeyBaby1
MokeyBaby2
MokeyBaby3
Cat
CatBaby1
I already have a method that tells me which ArrayList indexes are related.
For example: getRelatedIndexes("Monkey") would return 0,1,2,3 for the original list.
I just need to know if there is an easy way to move all the items up or down an ArrayList together.
Thanks.
You could wrap your list in a reorderable list and implement your reordering through that - at least you wouldn't need to hack the main list. It would maintain the order in an array of ints which you can then move around at will. You could even maintain the same data in several different orders if you like.
public static class OrderedList<T> extends AbstractList<T> {
// The list I proxy.
private final List<T> it;
// The order.
private final int[] order;
public OrderedList(List<T> wrap) {
it = wrap;
order = new int[it.size()];
// Initially the same order.
for (int i = 0; i < order.length; i++) {
order[i] = i;
}
}
#Override
public T get(int index) {
return it.get(order[index]);
}
#Override
public int size() {
return it.size();
}
// TODO - Only moves up! Breaks on a down move.
public void move(int start, int length, int to) {
int[] move = new int[length];
// Copy it out.
System.arraycopy(order, start, move, 0, length);
// Shift it down.
System.arraycopy(order, start + length, order, start, to - start);
// Pull it back in.
System.arraycopy(move, 0, order, to, length);
}
}
public void test() {
List<String> t = Arrays.asList("Zero", "One", "Two", "Three", "Four", "Five");
OrderedList<String> ordered = new OrderedList(t);
System.out.println(ordered);
ordered.move(1, 2, 3);
System.out.println(ordered);
}
prints
[Zero, One, Two, Three, Four, Five]
[Zero, Three, Four, One, Two, Five]
Alternatively - use Collections.rotate and work out what sub-list should be rotated which way to achieve your move.
Perhaps this contains the solution you need (swap and/or rotate/sublist) - Moving items around in an ArrayList
You could search for items in your list, which fullfill your criteria and safe them into another temporary list. Then you use the addAll(int index, Collection<? extends E> c) method to add these elements again to the list. Then you do not have to use add(int index, E element) for every Element itsself.
The block shifting strategy can be achieved by
taking the elements out of the original list using List.remove(index) and adding into a new temporary array. Note this has to be done in reverse order otherwise the indexes will change as items are removed.
Adding the new temporary array into the desired location using List.addAll(index, collection)
list of indexes cloned in case it is being used elsewhere
Example
public static void main(String[] args) {
List<Animal> animals = new ArrayList<Animal>(Arrays.asList(new Animal(
"Mokey"), new Animal("MokeyBaby1"), new Animal("MokeyBaby2"),
new Animal("MokeyBaby3"), new Animal("Dog"), new Animal(
"DogBaby1"), new Animal("DogBaby2"), new Animal("Cat"),
new Animal("CatBaby1")));
int[] relatedIndexes= { 0, 1, 2, 3 };
shift(animals, relatedIndexes, 3);
System.out.println(animals);
}
private static void shift(List<Animal> original, int[] indexes, int newIndex) {
int[] sorted = indexes.clone();
Arrays.sort(sorted);
List<Animal> block = new ArrayList<Animal>();
for (int i = sorted.length - 1; i >= 0; i--) {
block.add(original.get(sorted[i]));
original.remove(i);
}
original.addAll(newIndex, block);
}
Output
[Dog, DogBaby1, DogBaby2, Mokey, MokeyBaby1, MokeyBaby2, MokeyBaby3, Cat, CatBaby1]

Reducing the number of comparisons performed when matching between two String arrays

I am comparing three arrays of Strings using the two classes below. Without using any hash maps or changing the structure of my code too much (I can't change the signature of findMatchingElements()), is there a way to minimize the number of comparisons that my method makes, in order to construct the new array of shared elements?
In TestRun.java I tested my code on three arrays with 8 elements each, which resulted in 46 comparisons made. I want to achieve a lower number of comparisons. Is there a way?
I tried using the remove() method to remove a string from the collection once it was successfully compared to a matching element from the query collection. That prevented some redundant comparisons, but it did not result in a significant reduction.
import java.util.*;
public class CommonElements {
int originalCollectionCount = 0;
Object[] originalCollections;
int listCount = 1;
int matchCount;
int comparisonCount = 0;
public Comparable[] findMatchingItems(Object[] collections)
{
String[] queryArray = (String[])collections[0];
String[] secondaryArray = (String[])collections[1];
ArrayList<String> queryList = new ArrayList(Arrays.asList(queryArray));
ArrayList<String> secondaryList = new ArrayList(Arrays.asList(secondaryArray));
ArrayList<String> commonList = new ArrayList();
int i = 0;
if(listCount == 1){
originalCollectionCount = collections.length;
originalCollections = collections;
}
listCount ++;
for(String x:queryList)
{
for(String y:secondaryList)
{
comparisonCount++;
if(x.compareTo(y) == 0)
{
commonList.add(x); //add mutually shared item to commonList
secondaryList.remove(y); //remove mutually shared item from consideration
if(originalCollectionCount == listCount) //if every list has been examined
{
System.out.println(commonList.get(i));
}
i++;
break;
}
}
}
String[] commonListResult = new String[commonList.size()];
commonList.toArray(commonListResult);
if(originalCollectionCount > listCount){
findMatchingItems(new Object[] {commonListResult,originalCollections[listCount]});}
if (collections.length == 0) {
return new Comparable[0];
} else if (collections.length == 1) {
return (Comparable[]) collections[0];
}
return commonListResult;
}
public int getComparisons(){
return comparisonCount;}
}
public class TestRun {
private final static String[] COLLECTION_5_1 = {"Pittsburgh", "New York", "Chicago", "Cleveland", "Miami", "Dallas", "Atlanta", "Detroit"};
private final static String[] COLLECTION_5_2 = {"Dallas", "Atlanta", "Cleveland", "Chicago", "Washington", "Houston", "Baltimore", "Denver"};
private final static String[] COLLECTION_5_3 = {"Chicago", "Kansas City", "Cleveland", "Jacksonville", "Atlanta", "Tampa Bay", "Dallas", "Seattle"};
public static void main(String[] args) {
new TestRun();
}
public TestRun() {
CommonElements commonElements = new CommonElements();
Object[] input = new Object[3];
input[0] = COLLECTION_5_1;
input[1] = COLLECTION_5_2;
input[2] = COLLECTION_5_3;
System.out.println("Matching items:");
commonElements.findMatchingItems(input);
System.out.println(commonElements.comparisonCount + " comparisons made.");
}
}
You could run a single advanced for loop as below provide the length for both array are same if not run throug it accordingly.
for(String str:arrayStr1){
if(arrayStr2.contains(str)){
newArray.add(str);
}
}
List<String> list_5_1 = new ArrayList<>(Arrays.asList(COLLECTION_5_1));
//[Pittsburgh, New York, Chicago, Cleveland, Miami, Dallas, Atlanta, Detroit]
List<String> list_5_2 = new ArrayList<>(Arrays.asList(COLLECTION_5_2));
//[Dallas, Atlanta, Cleveland, Chicago, Washington, Houston, Baltimore, Denver]
list_5_1.retainAll(list_5_2);
//[Chicago, Cleveland, Dallas, Atlanta]
We have to pass list returned from Arrays.asList, as Arrays.asList method returns only immutable list.
am comparing three arrays of Strings using the two classes below. Without using any hash maps or changing the structure of my code too much (I can't change the signature of findMatchingElements()), is there a way to minimize the number of comparisons that my method makes, in order to construct the new array of shared elements?
Sure. Your nested loops have complexity of O(m*n). When you create a temporary HashMap, you can reduce it to O(m+n) and gain a lot for big inputs. From practical POV, somewhere around length of 10 it should get faster than your solution.
I'm giving no code as it's too straightforward.

How to reverse a String array based on it's index value

I want to reverse this:
customArray = myArray;
nameArray = new String[myArray.size()];
for (int i = 0; i < myArray.size(); i++) {
idArray[i] = myArray.get(i).getUnitID();
nameArray[i] = myArray.get(i).getUnitName();
}
if (sortType == SORT)
Arrays.sort(idArray, Collections.reverseOrder());
Arrays.sort(nameArray, ...);
I know how to reverse this by using Arrays.sort(stringArray, Collections.reverseOrder()); but not by index value.
How can I reverse the order of my nameArray based on it's nameArray[i]?.. or even better, since my idArray is actually a list of unique ID's I would like to sort nameArray based on the idArray.
(Update: Now reversing an array of strings and not a list of strings.)
Use Collections.reverse together with Arrays.asList to reverse the array.
Example:
public static void main(String... args) {
String[] array = {"one", "two", "three"};
Collections.reverse(Arrays.asList(array)); // reverses the underlying list
System.out.println(Arrays.toString(array)); // prints [three, two, one]
}
The only solution to your problem is to use an OOP approach. After all, Java is an OOP Language. So instead of having two arrays:
int[] unitID
String[] unitName
which you can't sort in a way that the indexes stay corresponding, write a class Unit that implements Comparable<Unit> and use one array:
Unit[] units
then
Arrays.sort(units);
will do the job.
You would need your own class:
public class Unit implements Comparable<Unit> {
private int id;
private String name;
// Constructor
// Methods
#Override
public int compareTo(Unit other) {
// sorts by id
return this.id - other.id;
// to sort by name, use this:
// return this.name.compareTo(other.name);
}
}
Try this:
import java.util.Collections;
import java.util.List;
import java.util.Arrays;
public void reverseString(String[] stringArray){
List<String> list = Arrays.asList(stringArray);
Collections.reverse(list);
stringArray= (String[]) list.toArray();
}
String[] yourStringArray = new String[]{"Sunday", "Monday", "Tuesday", "Wednesday"};
reverseString(yourStringArray);
//result would be:
//"Wednesday","Tuesday", "Monday", "Sunday"

Categories