How to copy a Set<Set<Integer>> into a List<Integer[]> in Java? - 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());

Related

Limit the number of occurrences for each Array's element to 2

I was attempting to solve this question in a Test.
It asked to make sure that an array could hold maximum of 2 repeated elements, if any element is occurring more than twice, that should be removed.
Given - [2, 2, 2, 3, 4, 4, 5]
Expected - [2, 2, 3, 4, 4, 5]
So, I tried with this approach as follows -
// 1. HashMap to count frequency of each element Key - Element, Value - Frequency
Map<Integer, Integer> data = new HashMap<Integer, Integer>();
for(int index = 0; index < n; index++) {
if(data.containsKey(arr[index])) {
data.put(arr[index], data.get(arr[index]) + 1);
}
else {
data.put(arr[index], 1);
}
}
// 2. Find most frequent element
int max_count = 0, need_to_remove = 0;
for(Entry<Integer, Integer> value : data.entrySet()) {
if(max_count < value.getValue()) {
need_to_remove = value.getKey();
max_count = value.getValue();
}
}
System.out.println("Max Count: " + max_count + " , Remove one occurrence of: " + need_to_remove);
//Output - Max Count: 3 , Remove one occurrence of : 2
Since, Value - 2 with Frequency - 3, need to remove a '2' from the map
// 3. Remove 'need_to_remove' value from map
map.remove(need_to_remove);
But doing so, it removes all occurrences of Element - 2 from the map.
{3=1, 4=2, 5=1}
I'm not really sure, about what needs to be done from here.
In case if the order of the elements should be preserved, then it would be correct to use List.remove() because this method removes the very first occurrence of the given element. Also, removal of elements from a List has the worst case time complexity O(n), which leads to overall quadratic timecomplexity O(n^2). We can do better.
Instead, you can build and a new List while iterating over the given array. And simultaneously, you need to track the occurrences of the previously encountered element via a HashMap. If the number of occurrences of a current element hasn't exceeded the limit, it should be added to the resulting list, otherwise ignored.
Note: that this solution would run in O(n), since we're iterating the list twice: to generate the list. And then to turn it into an array, all the action needs to be performed during iterations run in O(1).
That's how it might be implemented:
public static int[] removeOccurrencesAboveLimit(int[] arr, int limit) {
Map<Integer, Integer> occurrences = new HashMap<>();
List<Integer> result = new ArrayList<>();
for (int next : arr) {
int freq = occurrences.merge(next, 1, Integer::sum); // returns a new Value (i.e. updated number of occurrences of the current array element)
if (freq <= limit) result.add(next);
}
return toArray(result);
}
public static int[] toArray(List<Integer> list) {
return list.stream().mapToInt(i -> i).toArray();
}
main()
public static void main(String[] args) {
int[] arr = {3, 1, 2, 1, 3, 3, 4, 4, 5, 1, 3, 5};
System.out.println(Arrays.toString(removeOccurrencesAboveLimit(arr, 2)));
}
Output:
[3, 1, 2, 1, 3, 4, 4, 5, 5]
Create a new, empty list (lets call it dest) and a Map of value and frequency (lets call this one freq).
Populate the dest list by iterating over the data list.
As you iterate over the list, update freq and increment the value of the key that corresponds to the value in data.
If freq.get(value) is greater than 2, then don't copy it to dest.
Once you have completely iterated over the data list, dest should contain the values with no more than 2 instances of a given value.
Additionally, freq should contain the total count of the number of times that the key occurred in data.
This makes it so that you don't need to mutate the data list in place - you're just copying it and only copying at most 2 of a given value.
If the input is a list, the frequency map should be used just to track the number of occurrences using method Map::merge. The repeated elements can be removed from the list if an iterator is used to traverse the list, and Iterator has method remove():
List<Integer> list = new ArrayList<>(Arrays.asList(2, 2, 2, 3, 4, 4, 5));
Map<Integer, Integer> freq = new HashMap<>();
for (Iterator<Integer> it = list.iterator(); it.hasNext(); ) {
if (freq.merge(it.next(), 1, Integer::sum) > 2) {
it.remove();
}
}
System.out.println(list); // -> [2, 2, 3, 4, 4, 5]
If the input is provided as an array, then a new resized array should be created and appropriate elements need to be copied.
int[] arr = {2, 2, 2, 3, 3, 4, 4, 2, 3, 5, 6, 4, 2};
Map<Integer, Integer> freq2 = new HashMap<>();
int i = 0;
for (int n : arr) {
if (freq.merge(n, 1, Integer::sum) <= 2) {
arr[i++] = n;
}
}
arr = Arrays.copyOf(arr, i);
System.out.println(Arrays.toString(arr)); // -> [2, 2, 3, 3, 4, 4, 5, 6]
The java.util.HashMap.remove() is used to remove the mapping of any particular key from the map. It basically removes the values for any particular key in the Map.
https://www.geeksforgeeks.org/hashmap-remove-method-in-java/
assuming that map is your data hashMap. and you want to reduce the count of value in your key, here is 2. so you should set the value again by getting the value and reducing it. like this
map.put(key, map.get(key) - 1);
however you should remove one key in your arr. instead of your map.
arr.remove(need_to_remove);
By the way you can solve your problem better.
List<Integer> list = new ArrayList<>(Arrays.asList(2, 2, 2, 3, 4, 4, 5));
Map<Integer,Long> resultMap= list.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
resultMap.entrySet()
.stream()
.forEach(entry -> {
if(entry.getValue() > 2){
list.remove(entry.getKey());
}
});
System.out.println(list.toString());

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

Split a list into sublists in java using if possible flatMap [duplicate]

This question already has answers here:
How to split array list into equal parts?
(9 answers)
Closed 2 years ago.
here is my list:
List<Integer> mylist = Arrays.asList(1,2,3,4,5,6,7,8,9,10,11,12);
Assuming my list is always even, then i would like to split it in 6 equal parts.
List as a Sketch:
[1,2,3,4,5,6,7,8,9,10,11,12]
Output Sketch:
[[1,2][3,4],[5,6],[7,8],[9,10],[11,12]]
I would prefer a solution if possible with Java 8 stream flatMap
Given that the "sublists" are all of equal size and that you can divide the list into exact sublists of the same size, you could calculate the desired size and then map an IntStream to the starting indexes of each sublist and use that to extract them:
List<Integer> mylist = Arrays.asList(1,2,3,4,5,6,7,8,9,10,11,12);
int size = mylist.size();
int parts = 6;
int partSize = size / parts;
List<List<Integer>> result =
IntStream.range(0, parts)
.mapToObj(i -> mylist.subList(i * partSize, (i + 1) * partSize)))
.collect(Collectors.toList());
EDIT:
IdeOne demo graciously provided by #Turing85
I know it out of the scope but another possibility is using a library like Guava which has a lot of methods related to List.
Dependency
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1-jre</version>
</dependency>
Example
package com.stackoverflow.question;
import java.util.Arrays;
import java.util.List;
import com.google.common.collect.Lists;
public class Question {
public static void main(String[] args) {
List<Integer> mylist = Arrays.asList(1,2,3,4,5,6,7,8,9,10,11,12);
List<List<Integer>> partionList = Lists.partition(mylist, 2);
System.out.println(partionList);
}
}
Output
[[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]]
Stream-operations should be stateless. The task to perform inherently needs state. Thus, I would recommend not using Stream. Instead, I would recommend a for-loop based solution:
public static List<List<Integer>> partition(
List<Integer> list,
int partitionSize) {
final int listSize = list.size();
if (listSize % partitionSize != 0) {
throw new IllegalArgumentException("The size of the list must be "
+ "divisible without remainder by the partition size.");
}
final List<List<Integer>> partition = new ArrayList<>();
for (int start = 0; start < listSize; start += partitionSize) {
partition.add(list.subList(start, start + partitionSize));
}
return partition;
}
Ideone demo
If we insist on using a Stream-based implementation, I would suggest using the code presented Mureinik's answer.
The following will work for any group size from 1 to the size of the list. If the group size does not evenly divided the list size then the remainder will be put into their own group.
Also, the extra map(ArrayList::new) ensures the sublist is put in its own list. Otherwise, changing the original list entries will also change the sublist entries.
int gsize = 2;
List<Integer> mylist =
Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
List<List<Integer>> subLists = IntStream
.iterate(0, i -> i < mylist.size(),
i -> i + gsize)
.mapToObj(i -> mylist.subList(i,
(i + gsize <= mylist.size()) ?
i + gsize : mylist.size()))
.map(ArrayList::new)
.collect(Collectors.toList());
System.out.println(subLists);
Prints
[[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]]
If the group size was 5, the new list would look like the following:
[[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12]]
If you truly want a solution with flatMap, you can try something like this, not an intuitive solution though. At the end it only need to return a Stream, so this is simply constructing sub lists as it progress and returning it as Stream at the end.
btw, I personally prefer Guava's Lists.partition
List result = list.stream().flatMap(new Function<>() {
List<List<Integer>> mainList = new ArrayList<>();
List<Integer> subList = new ArrayList<>(2);
int index = 0;
#Override
public Stream<?> apply(Integer integer) {
subList.add(integer);
if ((index + 1) % 2 == 0) {
mainList.add(subList);
subList = new ArrayList<>(2);
}
index++;
if (index == list.size()) {
if(!subList.isEmpty()) {
mainList.add(subList);
}
return mainList.stream();
}
return Stream.empty();
}
}).collect(Collectors.toList());

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

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]]

HashMap wrong values for keys

I am kinda new to Java, and I am trying to write a function that maps all element indexes from an ArrayList into a HashMap, so I can easily see the indexes of duplicate elements.
The code below works , but when I try to print the values using the second for, it shows completely different results!
Example:
60 [40, 64]
What the 2nd for shows
60 [64]
more numbers
60 [64]
HashMap<Integer,ArrayList<Integer>> table= new HashMap<Integer,ArrayList<Integer>>();
//checking all items in an ArrayList a
//and putting their index in a hashTable
for(int i=0; i<a.size(); i++){
ArrayList<Integer> indexes = new ArrayList<Integer>();
indexes.add(i);
for(int j=i+1; j<a.size(); j++){
//are the items equal?
if(a.get(j).equals(a.get(i))){
indexes.add(j);
}
}
//put in the HashMap
table.put(a.get(i), indexes);
System.out.println(a.get(i) + " " +table.get((a.get(i))));
}
//shows completely different results!
for(int ix=1;ix<table.size();ix++)
System.out.println(a.get(ix) + " " +table.get(a.get(ix)));
Try this:
public static void main(String[] args) {
List<Integer> input = Arrays.asList(60, 60, 1, 4, 5, 7, 60);
Map<Integer, List<Integer>> result = new HashMap<>();
for (int n = 0; n < input.size(); ++n) {
List<Integer> list = result.get(input.get(n));
if (list != null) {
list.add(n);
} else {
list = new ArrayList<>();
list.add(n);
result.put(input.get(n), list);
}
}
System.out.println(result); // prints {1=[2], 4=[3], 5=[4], 7=[5], 60=[0, 1, 6]}
}
But I don't get it...What did I do wrong? As far as I see, my code is really inefficient compared to yours, but shouldn't it do the same thing?
Well no. In addition to being inefficient, your version has a significant bug.
Lets take your example input {60, 60, 1, 4, 5, 6, 7, 60}.
First iteration the loop, you build a list containing {0, 1, 7} and put it into the map so that we have map containing{ 60 -> {0, 1, 7} }`
Second iteration of the loop, we build a list containing {1, 7} and put it into the map. But this of course replaces the original (correct) list for 60 ... and we end up with { 60 -> {1, 7} }
And so on. In short, your version will end up producing a map that maps from the values to a list containing just the last index of that value.

Categories