Deepak Bisht
Deepak Bisht

Reputation: 53

How to randomize the same values in Hashmap in java

 Map<Integer, BigDecimal> vendorRating = new HashMap<>();
List<Integer> sortedList = new LinkedList<>();

vendorRating has values (11=>4,12=>3.5,13=>3,14=>3.5,15=>4,16=>5)

I want to sort the key based on value and shuffle the same value.

Output should be like [16,11,15,12,14,13], [16,15,11,14,12,13],[16,15,11,12,14,13], [16,11,15,14,12,13]

I have tried as below, this is working for sorting but i don't know i to shuffel the same value.

  vendorRating.entrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
            .forEachOrdered(
                x -> sortedList.addAll(Collections.singleton(x.getKey()))
        );

Upvotes: 1

Views: 142

Answers (1)

Eritrean
Eritrean

Reputation: 16498

Assuming you have a map like:

Map<Integer, BigDecimal> vendorRating = Map.of( 11, BigDecimal.valueOf(4),
                                                12, BigDecimal.valueOf(3.5),
                                                13, BigDecimal.valueOf(3),
                                                14, BigDecimal.valueOf(3.5),
                                                15, BigDecimal.valueOf(4),
                                                16, BigDecimal.valueOf(5));

You first need a comparator to sort in descending order as desired:

Comparator<Map.Entry<Integer, BigDecimal>> comp = Map.Entry.comparingByValue();

Using above comparator in a stream you can then use Collectors.groupingBy to group the entries having the same value to produce a Map<BigDecimal,List<Integer>>

Map<BigDecimal,List<Integer>> grouped =
            
    vendorRating.entrySet()
            .stream()
            .sorted(comp.reversed())
            .collect(Collectors.groupingBy(Map.Entry::getValue, LinkedHashMap::new,
                    Collectors.mapping(Map.Entry::getKey, Collectors.toList())));
    
    System.out.println(grouped);

//output: {5=[16], 4=[11, 15], 3.5=[12, 14], 3=[13]}

As you seem to only be interested in the keys of your original map and not the BigDecimal values I'll use below only the values of the above map grouped.values(), i.e [[16], [11, 15], [12, 14], [13]]

And here comes the intersting part. Now you need to geerate all permutations of each list which is not a trivial task but possible to write an algorithm by yourself to do so. But many libraries like Guava, Apache commons have already such functionalities. I recomend using a lib instead of implementing a permutation generating algorithm. I will use combinatoricslib3 which I find very handy when working with streams.

Maven dependency:

<dependency>
   <groupId>com.github.dpaukov</groupId>
   <artifactId>combinatoricslib3</artifactId>
   <version>3.3.2</version>
</dependency>

As you can see the examples in the linked page above with combinatoricslib3 you can generate permutations very easily and the syntax is simple, Example to generate permutations of some strings

Generator.permutation("apple", "orange", "cherry")
   .simple()
   .stream()
   .forEach(System.out::println);

//will output

[apple, orange, cherry]
[apple, cherry, orange]
[cherry, apple, orange]
[cherry, orange, apple]
[orange, cherry, apple]
[orange, apple, cherry]

Applied to your usecase, generating permutations of each sublist could look like

grouped.values().stream()
            .map(list -> Generator.permutation(list).simple().stream().collect(Collectors.toList()))
            .collect(Collectors.toList()).forEach(System.out::println);

will give you :

[[16]]
[[15, 11], [11, 15]]
[[12, 14], [14, 12]]
[[13]]

which is close to your desired end result but not quite there. You need now to pick one element at a time from each sublist to get your end result, which is the same as producing the Cartesian Product of the lists. And luckily this is also possible with combinatoricslib3. But I am sure other libs like Guava have implementations for the same. Syntax is like above very simple. Look at the their examples if you need more info.

First storing the intermediate result in a list of lists:

List<List<List<Integer>>> perms = grouped.values().stream()
                                            .map(list -> Generator.permutation(list)
                                                    .simple().stream()
                                                    .collect(Collectors.toList()))
                                            .collect(Collectors.toList());

//and generating the cartasian product

Generator.cartesianProduct(perms)
            .stream()
            .forEach(System.out::println);

//should produce something like:

[[16], [15, 11], [14, 12], [13]]
[[16], [15, 11], [12, 14], [13]]
[[16], [11, 15], [14, 12], [13]]
[[16], [11, 15], [12, 14], [13]]

Which is almost your end result. You need to only stream over the inner lists and flatmap to collect them in one list:

Generator.cartesianProduct(perms)
            .stream()
            .map(lili -> lili.stream().flatMap(List::stream).collect(Collectors.toList()))
            .collect(Collectors.toList())
            .forEach(System.out::println);

will finaly get you to your destination:

[16, 15, 11, 14, 12, 13]
[16, 15, 11, 12, 14, 13]
[16, 11, 15, 14, 12, 13]
[16, 11, 15, 12, 14, 13]

I break the whole task in smaller steps in order to explain it easly. If you are not interested in storing intermadiate results but only in the final output you can inline the steps and make it in one go. A working demo:

import java.math.BigDecimal;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.paukov.combinatorics3.Generator;

public class Example {

    public static void main(String[] args) {
        Map<Integer, BigDecimal> vendorRating = Map.of( 11, BigDecimal.valueOf(4),
                                                        12, BigDecimal.valueOf(3.5),
                                                        13, BigDecimal.valueOf(3),
                                                        14, BigDecimal.valueOf(3.5),
                                                        15, BigDecimal.valueOf(4),
                                                        16, BigDecimal.valueOf(5));

        Comparator<Map.Entry<Integer, BigDecimal>> comp = Map.Entry.comparingByValue();
        
        List<List<Integer>> result =
        Generator.cartesianProduct(
                vendorRating.entrySet()
                        .stream()
                        .sorted(comp.reversed())
                        .collect(Collectors.groupingBy(Map.Entry::getValue, LinkedHashMap::new,
                                Collectors.mapping(Map.Entry::getKey, Collectors.toList())))
                        .values().stream()
                        .map(list -> Generator.permutation(list).simple().stream().collect(Collectors.toList()))
                        .collect(Collectors.toList())
        ).stream()
                .map(lili -> lili.stream().flatMap(List::stream).collect(Collectors.toList()))
                .collect(Collectors.toList());

        result.forEach(System.out::println);        
    }
}

output

[16, 15, 11, 14, 12, 13]
[16, 15, 11, 12, 14, 13]
[16, 11, 15, 14, 12, 13]
[16, 11, 15, 12, 14, 13]

The answer has become much longer than originally thought. Here is a potato for the long post

System.out.println("Potato");

Upvotes: 1

Related Questions