Generation of child lists from a mother list - java

I have a list of N objects in input and I would like to group in daughter lists the objects of the same family. Objects in the same family share the attribute "File_identifier". Finally I would like to access these daughter lists via a key being an attribute of one of the objects of the daughter list.
SentinelReportModels is the parent list
HashMap<String, List<SentinelReportModel>> hashmap = new HashMap<String, List<SentinelReportModel>>();
for (int i = 0; i < sentinelReportModels.size(); i++) {
for (int k = 0; k < sentinelReportModels.size(); k++) {
if (sentinelReportModels.get(i).getIdentifiantfichier()
.equals(sentinelReportModels.get(k).getIdentifiantfichier()) ) {
ArrayList<SentinelReportModel> listeTemp = new ArrayList<>();
listeTemp.add(sentinelReportModels.get(i));
listeTemp.add(sentinelReportModels.get(k));
hashmap.put(sentinelReportModels.get(i).getTypeflux(),listeTemp);
}
}
}
However it does not work, I get X lists with duplicates.

I believe the below code should do the trick. It is basically, putting all the setinels with same Flux in a list. If flux id is different a new list is added to the map.
HashMap<String, List<SentinelReportModel>> hashmap = new HashMap<String, List<SentinelReportModel>>();
hashmap.put(,listeTemp);
for (int i = 0; i < sentinelReportModels.size(); i++) {
if (hashMap.get(sentinelReportModels.get(i).getTypeflux())==null) {
ArrayList<SentinelReportModel> list = new ArrayList<SentinelReportModel>();
list.add(sentinelReportModels.get(i));
hashMap.put(sentinelReportModels.get(i).getTypeflux(),list);
}else { hashMap.get(sentinelReportModels.get(i).getTypeflux()).add(sentinelReportModels.get(i));
}
}

This is easy to achieve with standard streams:
The group of child lists can be obtained using a group by:
Map<String, List<SentinelReportModel>> map =
sentinelReportModels.stream().collect(
Collectors.groupingBy(model -> model.getIdentifiantfichier()));
This will give you a map of <identifiantfichier, List<SentinelReportModel>>.
And this can be process normally as a string/list map.
Your inner loop has a flaw in the sense that it doesn't take into account that previous iterations may have already created a list for the current element's identifiantfichier and overwrites it anyway.

Related

Loop over 3 lists that is inside a map

I have a map which consists of 3 lists as follows.
User user = new User();
user.setId(1);
// more user creation
List<User> usersOne = Arrays.asList(user1, user2, user3);
// more lists created
// This is the map with 3 lists. Adding data to map
Map<String, List<User>> map = new HashMap<>();
map.put("key1", usersOne);
map.put("key2", usersTwo);
map.put("key3", usersThree);
Above is just to show example. This map is constructed this way and coming from a rest call.
Is there a way I could loop over those lists for all 3 keys and add per item to a new list?
Meaning like this.
List<User> data = new ArrayList<>();
List<User> list1 = map.get("key1");
List<User> list2 = map.get("key2");
List<User> list3 = map.get("key3");
data.add(list1.get(0));
data.add(list2.get(0));
data.add(list3.get(0));
// and then
data.add(list1.get(1));
data.add(list2.get(1));
data.add(list3.get(1));
and so on. Or a better way to do it. Ultimate looking to get a new list of the users by getting them in this manner, 1 from each list and then move on to next index.
Note that the 3 lists are not of same length.
Was looking to see if I could achieve it via something like the following.
But looks expensive. Could I get some advice on how I could achieve this?
for (User list1User : list1) {
for (User list2User : list2) {
for (User list3User: list3) {
// write logic in here since I now do have access to all 3 lists.
// but is expensive plus also going to run into issues since the length is not the same for all 3 lists.
}
}
}
Simple solution for your problem is "use index".
int size1 = list1.size();
int size2 = list2.size();
int size3 = list3.size();
//Choose maxSize of (size1,size2,size3)
int maxSize = Math.max(size1,size2);
maxSize = Math.max(maxSize,size3);
then use single loop, only add with condition i < listSize:
for(int i=0;i<maxSize;i++){
if(i < size1) data.add(list1.get(i);
if(i < size2) data.add(list2.get(i);
if(i < size3) data.add(list3.get(i);
}
In case you have more than 3 list in map:
Collection<List<Object>> allValues = map.values();
int maxSize = allValues.stream().map(list-> list.size()).max(Integer::compare).get();
List<Object> data = new ArrayList<>();
for(int i=0;i<maxSize;i++) {
for(List<Object> list: allValues) {
if(i < list.size()) data.add(list.get(i));
}
}
Ps: you might get warning because i am using notepad++ not editor tool.
Not very simple but very concise and pragmatic would be to extract the lists from the map, flatten them and then collect them. Since Java 8 we can utilize streams for that.
List<User> data = map.values().stream()
.flatMap(users -> users.stream())
.collect(Collectors.toList());
With .values() you get a Set<List<User>> of your UserLists, i assume, that you don't need the keys.
With .flatMap() we gather all users from each list. And we use flatMap() instead of map() because we want to retrieve the users from each list.
The last method-call .collect(Collectors.toList() collects all elements from this stream and puts them into a read-only-list.
In this case, you would preserve multiple occurrences of the same user, which you wouldn't if you collect them as a set collect(Collectos.asSet()), given they are really different objects.

Best way to get unique list without changing the Order

I have a list of string or list of integers of 20,000 items
Now it contains duplicates...However i don't want to disturb the order of the item.
We can easily convert a list to Set for unique Set unique = new HashSet(list);
However the above breaks the sequential order of the items.
What would be the best approach for this?
Thanks.
You should use java.util.LinkedHashSet to get unique elements without changing the order:
Set<String> uniqueSet = new LinkedHashSet<>(list);
One other way is to use distinct():
list.stream().distinct().collect(Collectors.toList())
But distinct() uses LinkedHashSet internally. There is no need for unnecessary procedure.
So best way is using the LinkedHashSet constructor:
LinkedHashSet(Collection c) Constructs a new linked hash
set with the same elements as the specified collection.
You can try stream distinct
yourList.stream().distinct().collect(Collectors.toList());
Update1:
As I know, this is the best solution.
list.contains(element) will do 2 loop processes. One for iterate the element and add it to new list, one for check element is contained -> 0(n*n)
new LinkedHashSet() will created a new LinkedHashSet, and a new Arraylist output -> issue about memory. And the performance, i think it is equals with stream distinct
Update2: we must ensure that the output is a List, not a Set
As I know, stream distinct use HashSet internally. It is an more efficient memory implementation than LinkedHashSet (which is hash table and linked list implementation of the set interface) in our case.
Detail here
If you apply LinkedHashSet, the source code will something like below, so we have 1 ArrayList and 1 LinkedHashSet.
output = new ArrayList(new LinkedHashSet(yourList));
I did a small benchmark with 1k for-loop.
int size = 1000000;
Random rand = new Random((int) (System.currentTimeMillis() / 1000));
List<Integer> yourList = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
yourList.add(rand.nextInt(10000));
}
// test1: LinkedHashSet --> 35ms
new ArrayList<Integer>(new LinkedHashSet<Integer>(yourList));
// test2: Stream distinct --> 30ms
yourList.stream().distinct().collect(Collectors.toList());
If you don't want to break the order, then Iterate the list and make a new list as below.
ArrayList<Integer> newList = new ArrayList<Integer>();
for (Integer element : list) {
if (!newList.contains(element)) {
newList.add(element);
}
}
Try the bellow code
public static void main(String[] args) {
String list[] = {"9","1","1","9","2","7","2"};
List<String> unique = new ArrayList<>();
for(int i=0; i<list.length; i++) {
int count = unique.size();
if(count==0) {
unique.add(list[i]);
}else {
boolean available = false;
for(int j=0; j<count; j++) {
if(unique.get(j).equals(list[i])) {
available = true;
break;
}
}
if(!available) {
unique.add(list[i]);
}
}
}
//checking latest 'unique' value
for(int i=0; i<unique.size(); i++) {
System.out.println(unique.get(i));
}
}
It will return 9 1 2 7, but I haven't tried up to 20,000 collection lists, hopefully there are no performance issues
If you are trying to eliminate duplicates, you can use LinkedHashSet it will maintain the order.
if String
Set<String> dedupSet = new LinkedHashSet<>();
if Integer
Set<Integer> dedupSet = new LinkedHashSet<>();

How can I check for duplicate values of an object in an array of objects, merge the duplicates' values, and then remove the duplicate?

Right now I have an array of "Dragon"s. Each item has two values. An ID and a Count. So my array would look something like this:
Dragon[] dragons = { new Dragon(2, 4),
new Dragon(83, 199),
new Dragon(492, 239),
new Dragon(2, 93),
new Dragon(24, 5)
};
As you can see, I have two Dragons with the ID of 2 in the array. What I would like to accomplish is, when a duplicate is found, just add the count of the duplicate to the count of the first one, and then remove the duplicate Dragon.
I've done this sort of successfully, but I would end up with a null in the middle of the array, and I don't know how to remove the null and then shuffle them.
This is what I have so far but it really doesn't work properly:
public static void dupeCheck(Dragon[] dragons) {
int end = dragons.length;
for (int i = 0; i < end; i++) {
for (int j = i + 1; j < end; j++) {
if (dragons[i] != null && dragons[j] != null) {
if (dragons[i].getId() == dragons[j].getId()) {
dragons[i] = new Item(dragons[i].getId(), dragons[i].getCount() + dragons[j].getCount());
dragons[j] = null;
end--;
j--;
}
}
}
}
}
You should most probably not maintain the dragon count for each dragon in the dragon class itself.
That aside, even if you are forced to use an array, you should create an intermeditate map to store your dragons.
Map<Integer, Dragon> idToDragon = new HashMap<>();
for (Dragon d : yourArray) {
// fetch existing dragon with that id or create one if none present
Dragon t = idToDragon.computeIfAbsent(d.getId(), i -> new Dragon(i, 0));
// add counts
t.setCount(t.getCount() + d.getCount());
// store in map
idToDragon.put(d.getId(), t);
}
Now the map contains a mapping between the dragons' ids and the dragons, with the correct counts.
To create an array out of this map, you can just
Dragon[] newArray = idToDragon.values().toArray(new Dragon[idToDragon.size()]);
You may be force to store the result in an array but that doesn't mean that you're force to always use an array
One solution could be using the Stream API, group the items adding the count and save the result into an array again. You can get an example of how to use the Stream API to sum values here. Converting a List<T> into a T[] is quite straightforward but anyways, you have an example here
The size of an array cannot be changed after it's created.
So you need to return either a new array or list containing the merged dragons.
public static Dragon[] merge(Dragon[] dragonArr) {
return Arrays.stream(dragonArr)
// 1. obtain a map of dragon IDs and their combined counts
.collect(groupingBy(Dragon::getId, summingInt(Dragon::getCount)))
// 2. transform the map entries to dragons
.entrySet().stream().map(entry -> new Dragon(entry.getKey(), entry.getValue()))
// 3. collect the result as an array
.toArray(Dragon[]::new);
}

How to move data from multiple Arraylist to multiple Arrays (in Java)

I have 3 arraylist each have size = 3 and 3 arrays also have length = 3 of each. I want to copy data from arraylists to arrays in following way but using any loop (i.e for OR for each).
myArray1[1] = arraylist1.get(1);
myArray1[2] = arraylist2.get(1);
myArray1[3] = arraylist3.get(1);
I have done it manually one by one without using any loop, but code appears to be massive because in future I'm sure that number of my arraylists and arrays will increase up to 15.
I want to copy the data from arraylists to arrays as shown in the image but using the loops not manually one by one?
How about this?
List<Integer> arraylist0 = Arrays.asList(2,4,3);
List<Integer> arraylist1 = Arrays.asList(2,5,7);
List<Integer> arraylist2 = Arrays.asList(6,3,7);
List<List<Integer>> arraylistList = Arrays.asList(arraylist0, arraylist1, arraylist2);
int size = 3;
int[] myArray0 = new int[size];
int[] myArray1 = new int[size];
int[] myArray2 = new int[size];
int[][] myBigArray = new int[][] {myArray0, myArray1, myArray2};
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
myBigArray[i][j] = arraylistList.get(j).get(i);
}
}
To explain, since we want to be able to work with an arbitrary size (3, 15, or more), we are dealing with 2-dimensional data.
We are also dealing with array and List, which are slightly different in their use.
The input to your problem is List<Integer>, and so we make a List<List<Integer>> in order to deal with all the input data easily.
Similarly, the output will be arrays, so we make a 2-dimensional array (int[][]) in order to write the data easily.
Then it's simply a matter of iterating over the data in 2 nested for loops. Notice that this line reverses the order of i and j in order to splice the data the way you intend.
myBigArray[i][j] = arraylistList.get(j).get(i);
And then you can print your answer like this:
System.out.println(Arrays.toString(myArray0));
System.out.println(Arrays.toString(myArray1));
System.out.println(Arrays.toString(myArray2));
You need to have two additional structures:
int[][] destination = new int [][] {myArray1, myArray2,myArray3 }
List<Integer>[] source;
source = new List<Integer>[] {arraylist1,arraylist2,arraylist3}
myArray1[1] = arraylist1.get(1);
myArray1[2] = arraylist2.get(1);
myArray1[3] = arraylist3.get(1);
for (int i=0;i<destination.length;i++) {
for (int j=0;j<source.length;j++) {
destination[i][j] = source[j].get(i);
}
}
If you cannot find a ready made API or function for this, I would suggest trivializing the conversion from List to Array using the List.toArray() method and focus on converting/transforming the given set of lists to a another bunch of lists which contain the desired output. Following is a code sample which I would think achieves this. It does assume the input lists are NOT of fixed/same sizes. Assuming this would only make the logic easier.
On return of this function, all you need to do is to iterate over the TreeMap and convert the values to arrays using List.toArray().
public static TreeMap<Integer, List<Integer>> transorm(
List<Integer>... lists) {
// Return a blank TreeMap if not input. TreeMap explanation below.
if (lists == null || lists.length == 0)
return new TreeMap<>();
// Get Iterators for the input lists
List<Iterator<Integer>> iterators = new ArrayList<>();
for (List<Integer> list : lists) {
iterators.add(list.iterator());
}
// Initialize Return. We return a TreeMap, where the key indicates which
// position's integer values are present in the list which is the value
// of this key. Converting the lists to arrays is trivial using the
// List.toArray() method.
TreeMap<Integer, List<Integer>> transformedLists = new TreeMap<>();
// Variable maintaining the position for which values are being
// collected. See below.
int currPosition = 0;
// Variable which keeps track of the index of the iterator currently
// driving the iteration and the driving iterator.
int driverItrIndex = 0;
Iterator<Integer> driverItr = lists[driverItrIndex].iterator();
// Actual code that does the transformation.
while (driverItrIndex < iterators.size()) {
// Move to next driving iterator
if (!driverItr.hasNext()) {
driverItrIndex++;
driverItr = iterators.get(driverItrIndex);
continue;
}
// Construct Transformed List
ArrayList<Integer> transformedList = new ArrayList<>();
for (Iterator<Integer> iterator : iterators) {
if (iterator.hasNext()) {
transformedList.add(iterator.next());
}
}
// Add to return
transformedLists.put(currPosition, transformedList);
}
// Return Value
return transformedLists;
}

How to access array of linked list?

I've created an array of LinkedList of Connection objects using the second answer from here. That is, I've done:
LinkedList<Connection>[] map = (LinkedList<Connection>[]) new LinkedList[count];
However, I am confused as to how to access each element of the array (ie each LinkedList) and create a new node. Right now, I have:
for (int j = 0; j < numOfConnections; j++) {
map[j].add(new Connection(s.next(), s.nextDouble(), s.next()));
}
But I think this would only add a single new node to each LinkedList element of the Array. I want to loop through and add numOfConnections amount of nodes to each LinkedList element. For example, 3 nodes in map[0], 5 nodes in map[1], 2 nodes in map[2], etc.
On your example "3 nodes in map[0], 5 nodes in map[1], 2 nodes in map[2]" if numOfConnections is the amount of values you want to add to your LinkedList[k] shoudnt you map which list to add ? eg.: numOfConnections[] = {3, 5, 2};
for ( int k = 0; k < numOfConnections.length; k++ )
{
if (map[k] == null) map[k] = new LinkedList<Connection>();
for (int j = 0; j < numOfConnections[k]; j++)
{
map[k].add(new Connection(s.next(), s.nextDouble(), s.next()));
}
}
Since you have an array of LinkedList instances:
For every bucket in the array, you need to put a new LinkedList instance
You need to add Connection instances to each LinkedList, which is contained in a bucket in the array.
You are treating your array like a List, by trying to invoke add on it.
in your loop, do something like
LinkedList<Connection> list = map[j]; // get the list for this bucket in the array
if (list == null) // if there is no LinkedList in this bucket, create one
list = map[j] = new LinkedList<Connection>();
list.add(new Connection(...));
I would change your variable name map to something like lists because Java has a Map object, and it's confusing.

Categories