Delete duplicates from table (List<List<Integer>> table = new ArrayList ...) - java

I got table like
List<List<Integer>> table = new ArrayList<>()
Where List's within are table rows, i need to set all duplicate values ALL OVER THE TABLE to null, how can do it via only ArrayList && foreach loop || λ - expressions?
Must work somehow like this:
1 2 3 null 2 3
4 1 5 -> 4 null 5
1 6 9 null 6 9
Sorry for my poor English and thank you for fast response!

Using java-8,
//Find duplicates
Map<Integer, Long> counts =
table.stream()
.flatMap(Collection::stream)
.collect(Collectors.groupingBy(i->i, Collectors.counting()));
// and remove them
table.stream().forEach(row -> row.replaceAll(i-> counts.get(i) > 1 ? null : i));

Interpreting the (slightly unclear) question as a deduplication (removal rather than "nulling"):
List<List<Integer>> dedup = table.stream().distinct().collect(toList());
or java 7:
List<List<Integer>> dedup = new ArrayList<>(new LinkedHashSet<>(table));
which is generally more useful.
However, if nulling is really required:
Set<List<Integer>> set = new HashSet<>();
List<List<Integer>> dedup = table.stream()
.filter(list -> set.add(list) ? list : null)
.collect(toList());
The add() method of a Set returns true is adding the element changed the set - ie if it's an element not seen before.

Try this, this approach is to build an index map for all the elements present in the List and setting the value to null if more than one value found for a key
List<List<Integer>> table = new ArrayList<>();
table.add(Arrays.asList(1, 2, 3));
table.add(Arrays.asList(4, 1, 5));
table.add(Arrays.asList(1, 6, 9));
System.out.println("Before " + table);
Map<Integer, List<List<Integer>>> indices = new HashMap<>();
for (int i = 0; i < table.size(); i++) {
for (int j = 0; j < table.get(i).size(); j++) {
int el = table.get(i).get(j);
if (indices.get(el) == null) {
indices.put(el, new ArrayList<>());
}
indices.get(el).add(Arrays.asList(i, j));
}
}
indices.keySet().stream()
.filter(k -> indices.get(k).size() > 1)
.flatMap(k -> indices.get(k).stream())
.forEach(li -> table.get(li.get(0)).set(li.get(1), null));
System.out.println("After " + table);
output
Before [[1, 2, 3], [4, 1, 5], [1, 6, 9]]
After [[null, 2, 3], [4, null, 5], [null, 6, 9]]

Related

Java - compare 2 lists based on highest value and subsequent values?

I have two lists of:
List<Person> persons;
each Person has the attribute - score
e.g.
list1:
person.score = 3
person.score = 5
person.score = 8
list2:
person.score = 8
person.score = 4
person.score = 7
I want to compare each list to find the highest score in each. If both highest are the same, then I want to compare the second highest etc and so on.
How can I do so?
You can make a copy of each List, sort these copies, then compare them element by element.
Comparator<List<Person>> comp = (a, b) -> {
List<Person> s1 = a.stream().sorted(Comparator.comparingInt(Person::getScore).reversed()).toList(),
s2 = b.stream().sorted(Comparator.comparingInt(Person::getScore).reversed()).toList();
for (int i = 0; i < Math.min(a.size(), b.size()); i++) {
int c = Integer.compare(s1.get(i).getScore(), s2.get(i).getScore());
if (c != 0) return c;
}
return Integer.compare(a.size(), b.size());
};
Define a comparator to compare the lists. After, sorting each list, The comparator short circuits as soon corresponding scores are unequal, returning the value of the the last comparison. Otherwise, if the two lists are equal or if if their sizes are different, the shortest list will return as the smaller.
Details
Since the score is the target of the comparison, I decided to grab it early and place in int arrays which are sorted. Then I iterate over the arrays in reverse order to ensure I am testing the largest elements first (IntStreams can't take a comparator for sorting and I didn't want to box the int values).
Iterating over the lists, the scores are compared and the result is mapped to the stream. Then the result of the comparison (-1,0, or 1) is filtered to ignore zero values, capturing the result in an OptionalInt.
Then either the OptionalInt value is returned, or the comparison of the array sizes. Then, all comparisons being equal, the smaller array length would return -1, larger 1, and equal lengths 0
Comparator<List<Person>> listComp = (lst1, lst2) -> {
int[] scores1 = lst1.stream().mapToInt(Person::getScore).sorted()
.toArray();
int[] scores2 = lst2.stream().mapToInt(Person::getScore).sorted()
.toArray();
OptionalInt diff = IntStream
.iterate(Math.min(scores1.length-1, scores2.length-1),
i -> i >= 0, i -> i-1)
.map(i -> Integer.compare(scores1[i], scores2[i]))
.filter(delta -> delta != 0).findFirst();
return diff.orElse(Integer.compare(scores1.length, scores2.length));
};
Demos
Some Data - comparing every two lists in the list of lists to each other. For demo purposes, I am using a record with a modified toString().
record Person(int getScore) {
#Override
public String toString() {
return "%d".formatted(getScore);
}
}
List<List<Person>> lists = List.of(
// first is smaller
List.of(new Person(3), new Person(5), new Person(8)),
List.of(new Person(8), new Person(4), new Person(7)),
// equal lists
List.of(new Person(3), new Person(7), new Person(8)),
List.of(new Person(8), new Person(3), new Person(7)),
// first is smaller due to length
List.of(new Person(3), new Person(7), new Person(8)),
List.of(new Person(8), new Person(3), new Person(7),
new Person(2)));
To compare the lists, use int result = listComp.compare(list1, list2);
int v;
for (int i = 0; i < lists.size(); i += 2) {
System.out.println(lists.get(i)
+ ((v = listComp.compare(lists.get(i), lists.get(i + 1))) > 0
? " > "
: (v < 0) ? " < " : " == ")
+ lists.get(i + 1));
}
prints the following:
[3, 5, 8] < [8, 4, 7]
[3, 7, 8] == [8, 3, 7]
[3, 7, 8] > [8, 3, 7, 2]

Create a list of List from an Array

How can i create a list of List from Array eg:
int[] arr = {3, 1, 5, 8, 2, 4}.
Such that the lists in the List have only two elements eg:
[[3,1], [5,8], [2,4]].
So far i have tried code below but it return only lists with one element,I can't figure out where i went wrong.
class ListList {
public static List<List<Integer>> listOfList(int[] num){
List<List<Integer>> arrList = new ArrayList<>();
for(int i = 0 ; i<num.length;i++){
List<Integer> list = new ArrayList<>();
if(list.size() !=2){
list.add(num[i]);
}
arrList.add(list);
}
return arrList;
}
}
Result: [[3], [1], [5], [8], [2], [4]].
Here's a generic one:
var arr = new int[] {3, 1, 5, 8, 2, 4};
var batchSize = 2;
List<List<Integer>> lists = IntStream.range(0, arr.length)
.mapToObj(index -> Map.entry(index, arr[index]))
.collect(Collectors.groupingBy(e -> e.getKey() / batchSize))
.values().stream()
.map(entries -> entries.stream().map(Map.Entry::getValue).toList())
.toList();
System.out.println(lists);
Output:
[[3, 1], [5, 8], [2, 4]]
You are basically creating a mapping of index->value and subsequently grouping by the batchSize to make splits
You are creating an empty list on each iteration, then you check if its size != 2 (of course it is) and add 1 element, finally you add list with 1 element to result list, which is not what you need.
Move list creation out of loop and add elements to it. When its size == 2, add current list to result list and create a new one.
class ListList {
public static List<List<Integer>> listOfList(int[] num) {
List<List<Integer>> arrList = new ArrayList<>();
List<Integer> list = new ArrayList<>();
for(int i = 0; i < num.length; i++) {
if(list.size() == 2) {
arrList.add(list);
list = new ArrayList<>();
}
list.add(num[i]);
}
if(list.size() != 0) {
arrList.add(list);
}
return arrList;
}
}
If your input size can be odd, then you would also add list of length 1 to result list. If you don't want to, add aditional checks.
If you're certain that the list has an even number of values, you can do it like this.
create a list of lists.
taking two at a time, put each in a separate list
add that list to the list of lists
int [] arr ={3, 1, 5, 8, 2, 4};
List<List<Integer>> list = new ArrayList<>();
for (int i = 0; i < arr.length; i+=2) {
List<Integer> temp = Arrays.asList(arr[i], arr[i+1]);
list.add(temp);
}
System.out.println(list);
prints
[[3, 1], [5, 8], [2, 4]]
If you list is of odd length, change the assignment to
List<Integer> temp = arr.length - i >= 2 ? Arrays.asList(arr[i], arr[i+1]) :
Arrays.asList(arr[i]);
And here is a different take on the idea suggested by Dhrubajyoti Gogoi.
stream the indices.
use integer math to divide into groups, mapping to a list
and return the values as a collection of lists.
int groupSize = 2;
Collection<List<Integer>> result =
IntStream.range(0, arr.length)
.mapToObj(Integer::valueOf)
.collect(Collectors.groupingBy(
i -> i / groupSize,
Collectors.mapping(i -> arr[i],
Collectors.toList())))
.values();
Try this:
public static List<List<Integer>> listOfList(int[] num){
List<List<Integer>> arrList = new ArrayList<>();
for (int i = 0; i < num.length; i += 2) {
if (num.length > i + 1) {
arrList.add(List.of(num[i], num[i+1]));
} else {
arrList.add(List.of(num[i]));
}
}
return arrList;
}
In your example you always create a fresh list in the loop, so size is always 0 and never 2.

How to copy a Set<Set<Integer>> into a List<Integer[]> in Java?

I want to copy a Set<Set<Integer>> of array to a List of Integer[]. I want to do that because I want to change the first element of every array when it is 0 , and put it at the end.
I have manage to do that but in a List<Set> , and when looping throw , I can't make changes because the array are still a Set.
No other questions make this clear, instead they explained when we have only Set<SomeWrapperClass>
Maybe some other solutions? Remark: I cannot change the Set<Set<Integer>>
My Set:
Set<Set<Integer>> indexs = new HashSet<>();
I can convert to a List only in this way:
List<Set<Integer>> arrangedIndexOfCourses = new ArrayList<>();
static Set<Set<Integer>> coursesIndex = Sets.powerSet(Sets.newHashSet(1, 2, 3, 4, 5, 6, 7, 8, 9, 0));
static List<Set<Integer>> arrangedIndexOfCourses = new ArrayList<>();
The following method return all the occurence of unique numbers made from 0123456789 ( Guava library )
private void indexSet(Set<Set<Integer>> coursesIndex) {
Set<Set<Integer>> indexs = new HashSet<>();
for (Set<Integer> token : coursesIndex) {
indexs.add(token);
arrangedIndexOfCourses.add(token);
count++;
}
}
And the code where I tried to change the first 0 of the arrays and put at last:
for (Set<Integer> i : arrangedIndexOfCourses) {
if (i.contains(0)) {
Collections.rotate(arrangedIndexOfCourses, -1);
}
}
It appears, the main trick in this task is how to implement rotation of the first element if it is 0.
As mentioned in the comments, not all types of sets maintain order and therefore have a "first" element. But upon converting a set to stream, even for a HashSet there's a first element in the stream unless it's empty.
Therefore, the set of integer sets may be converted to a list of integer arrays as follows:
Take the first element of the stream using limit or findFirst
Compare to 0, if needed put it to the end
Else keep the stream of the set as is
Set<Set<Integer>> data = Set.of(
Collections.emptySet(),
new LinkedHashSet<>(Arrays.asList(0, 1, 2, 3)),
new LinkedHashSet<>(Arrays.asList(4, 5, 6)),
new LinkedHashSet<>(Arrays.asList(10, 0, 100, 1000)),
new TreeSet<>(Arrays.asList(25, 0, 1, 16, 4, 9)),
new HashSet<>(Arrays.asList(0, 5, 50))
);
List<Integer[]> rotatedZeros = data
.stream()
.map(s ->
(s.stream().findFirst().orElse(-1) == 0 // or limit(1).findAny()
? Stream.concat(s.stream().skip(1), Stream.of(0))
: s.stream())
.toArray(Integer[]::new)
)
.collect(Collectors.toList());
rotatedZeros.stream().map(Arrays::toString).forEach(System.out::println);
Output:
[]
[4, 5, 6]
[1, 2, 3, 0]
[10, 0, 100, 1000]
[1, 4, 9, 16, 25, 0]
[50, 5, 0]
the 2-step solution with a lambda w/o external library
(1) convert the Set<Set<Integer>> to a List<Integer[]>
List<Integer[]> arrangedIndexOfCourses = coursesIndex.stream()
.map(s -> s.toArray(Integer[]::new)).collect(toList());
(2) iterate over the arrays and change the zero-arrays in the traditional way
for (Integer[] indices : arrangedIndexOfCourses) {
if (indices.length > 0 && indices[0] == 0) {
for (int i = 0; i < indices.length - 1; i++)
indices[i] = indices[i + 1];
indices[indices.length - 1] = 0;
}
}
a lambda based on Alex's mind blowing solution
moving the zero-rotating-stuff upstream makes the lambda less trickier and You can work with collections instead of arrays
List<Integer[]> arrangedIndexOfCourses = coursesIndex.stream()
.map(s -> {
if (s.stream().findFirst().orElse(-1) == 0) {
List<Integer> l = new ArrayList<>();
l.addAll(s);
l.remove(0);
l.add(0);
return l.toArray(Integer[]::new);
} else
return s.toArray(Integer[]::new);
}).collect(toList());

How to create a new List from merging 3 ArrayLists in round robin style?

I have 3 arrays. I want to merge the 3 arrays and create a new array that shows all the merged integers. I would like this new array to be in round robin style.
Example input:
array = {arr1 = 1, 2, 3, arr2 = 4, 5, 6, arr3 = 7, 8, 9}
Example output:
arr4 = 1, 4, 7, 2, 5, 8, 3, 6, 9
I'm supposed to use this function but I don't understand how can I use a listOfLists:
String roundRobin(List<List<Integer>>listOfLists) {
You could do it more simply using ArrayLists or Arrays.
With Arrays, you create 3 int[] and 1 int[][].
What this translates to is "3 arrays of ints and 1 array of arrays of ints". That is what #4 is: an array of your other arrays. In code it is:
int[]
arr1 = {1, 2, 3},
arr2 = {4, 5, 6},
arr3 = {7, 8, 9};
int[][] arr4 = {arr1, arr2, arr3};
Alternatively, you could use ArrayLists, which differ from Arrays in that you can add or remove elements, among many other actions. The logic is the same in this case, just the syntax is different. You create 3 ArrayList<Integer> and 1 ArrayList<ArrayList<Integer>>, which translates to "3 ArrayLists of Integers and 1 ArrayList of ArrayLists of Integers." In code it is:
ArrayList<Integer>
list1 = new ArrayList<>(Arrays.asList(1, 2, 3)),
list2 = new ArrayList<>(Arrays.asList(4, 5, 6)),
list3 = new ArrayList<>(Arrays.asList(7, 8, 9));
List<ArrayList<Integer>> list4 = new ArrayList<>
(Arrays.asList(list1, list2, list3));
Finally, you can print the output of both methods:
System.out.println("Arrays - int[][]: " + Arrays.deepToString(arr4)
+ "\nLists - List<ArrayList<Integer>>: " + list4);
And you will get:
Arrays - int[][]: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Lists - List<List<Integer>>: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Does this help?
You can use two nested for loops:
List<List<Integer>> lists = List.of(
List.of(1, 2),
List.of(3, 4, 5, 6),
List.of(7, 8, 9));
List<Integer> listRobin = new ArrayList<>();
// infinite loop through the columns
for (int i = 0; ; i++) {
// if the maximum number
// of columns is reached
boolean max = true;
// loop through the rows, aka inner lists
for (List<Integer> row : lists) {
// if this column is present
if (i < row.size()) {
// column is present
max = false;
// take value from the column
listRobin.add(row.get(i));
}
}
// while the columns are still present
if (max) break;
}
// output
System.out.println(listRobin);
// [1, 3, 7, 2, 4, 8, 5, 9, 6]
See also: Filling a jagged 2d array first by columns
I think just loop input and get element and add element to a list of array is easy to understand.
public static List<Integer> roundRobin(List<List<Integer>> listOfLists) {
if (listOfLists == null || listOfLists.isEmpty()) {
return new ArrayList<>();
}
int maxLength = -1;
for (List<Integer> list : listOfLists) {
if (list.size() > maxLength) {
maxLength = list.size();
}
}
List<Integer> result = new ArrayList<>(maxLength * listOfLists.size());
for (int i = 0; i < maxLength; i++) {
for (List<Integer> list : listOfLists) {
if (i < list.size()) {
result.add(list.get(i));
}
}
}
return result;
}
To populate a 1d list from the columns of a 2d list, you can use two nested streams: first by columns, and then by rows. The length of the internal lists does not matter, in an outer stream you can traverse while the columns are still present.
List<List<Integer>> listOfLists = List.of(
List.of(1, 2, 3, 4),
List.of(5, 6),
List.of(7, 8, 9));
List<Integer> listRobin = IntStream
// infinite Stream through
// the columns of a 2d list
.iterate(0, i -> i + 1)
// take the values from the column
// Stream<List<Integer>>
.mapToObj(i -> listOfLists
// iterate over the inner lists
.stream()
// take those lists where
// this column is present
.filter(list -> list.size() > i)
// take value from the column
.map(list -> list.get(i))
// return a new list
.collect(Collectors.toList()))
// while the columns are still present
.takeWhile(list -> list.size() > 0)
// flatten to a single stream
// Stream<Integer>
.flatMap(List::stream)
// return a new list
.collect(Collectors.toList());
// output
System.out.print(listRobin); // [1, 5, 7, 2, 6, 8, 3, 9, 4]
See also: Efficient way to choose data from several Lists with round robin algorithm

Retrieve all value pairs from List<Integer>

For example I have a List<Integer> object with the following:
3, 6, 5, 3, 3, 6
The result would be 3 and 6. How can I create a function that tests for duplicates and then returns 1 value of the duplicate (not the pair, just one in the pair)? One problem that might occur is if there are quadruple values: 3, 4, 5, 3, 8, 3, 3 Then I would like to return 3 and 3. How can I accomplish this?
I would go through the list counting the number of instances of each one (storing them in a map) and then create a new list from the map:
List<Integer> values = // the list of values you have
Map<Integer,Integer> counts = new HashMap<Integer,Integer>();
for(Integer value : values) {
if(counts.containsKey(value)) {
counts.put(value, counts.get(value)+1);
} else {
counts.put(value, 1);
}
}
List<Integer> resultValues = new ArrayList<Integer>();
for(Integer value : counts.keySet()) {
Integer valueCount = counts.get(value);
for(int i=0; i<(valueCount/2); i++) { //add one instance for each 2
resultValues.add(value);
}
}
return resultValues;
This avoids the O(nlogn) behavior of sorting the values first, working in O(n) instead.
I think the "map" way by #RHSeeger is good enough. Here I just suggest another way, just for 'fun', so that u may take a look. It kind of give a stable result: first completed pairs appears first:
List<Integer> values = ....;
List<Integer> result = new ArrayList<Integer>();
Set<Integer> unpairedValues = new HashSet<Integer>();
for (int i : values) {
if (unpairedValues.contains(i)) {
result.add(i);
unpairedValues.remove(i);
} else {
unpairedValues.add(i);
}
}
// result contains what u want
One possible way in pseudo code:
sortedList = sort (list)
duplicates = empty list
while (length (list) > 1)
{
if (list [0] == list [1] )
{
duplicates.append (list [0] )
list.removeAt (0)
}
list.removeAt (0);
}
// ArrayList<Integer> list = {3, 6, 5, 3, 3, 6}
Collections.sort(list);
ArrayList<Integer> pairs = new ArrayList<Integer>();
int last = Integer.MIN_VALUE; // some value that will never appear in the list
for (Integer cur : list) {
if (last == cur) {
pairs.add(cur);
last = Integer.MIN_VALUE; // some value that will never appear in the list
} else {
last = cur;
}
}
System.out.println(Arrays.toString(pairs.toArray()));
Will output
[3, 6]
** Edit **
Slightly better algorithm, modifies the given list
// ArrayList<Integer> list = {3, 6, 5, 3, 3, 6}
Collections.sort(list);
int index = 1, last;
while (index < list.size()) {
last = list.remove(index - 1);
if (list.get(index - 1) == last) {
index++;
}
if (index == list.size()) {
list.remove(index - 1);
}
}

Categories