Possible permutations of string based on LinkedHashMap? - java

So this problem has been taunting me for days. Any help is greatly appreciated!
I have made a LinkedHashMap which stores possible combinations for each part of a string and I'm trying to get all the permutations in an ArrayList of Strings, while maintaing the string order.
For example if the map is:
a=ab, b=c
The combinations would be:
ab
ac
abb
abc
I have tried simply looping each keys and values list, heap's algorithm which didn't work out for keeping the order of elements and also tried using recursion but i wasn't sure how. If anyone could point me in the right direction or hint me, that would be great. Thanks
Another map example of what im trying to do.
If the map is:
A=a, B=b
Output is:
AB (key1, key2)
Ab (key1, value2)
aB (value1, key2)
ab (value1, value2)
I basically want every combination of the whole map in order while alternating between keys and values of the map.

Try this.
static List<String> possiblePermutations(Map<String, String> map) {
int size = map.size();
List<Entry<String, String>> list = new ArrayList<>(map.entrySet());
List<String> result = new ArrayList<>();
new Object() {
void perm(int i, String s) {
if (i >= size) {
result.add(s);
return;
}
Entry<String, String> entry = list.get(i);
perm(i + 1, s + entry.getKey());
perm(i + 1, s + entry.getValue());
}
}.perm(0, "");
return result;
}
public static void main(String[] args) {
Map<String, String> map = new LinkedHashMap<>();
map.put("a", "ab");
map.put("b", "c");
map.put("x", "y");
List<String> result = possiblePermutations(map);
System.out.println(result);
}
output:
[abx, aby, acx, acy, abbx, abby, abcx, abcy]

It's very simple ...
Let's say the number of entries in your map is N
There is a 1-to-1 mapping between each possible permutation of such strings and an array boolean of length N. If the array has a true in position K, we pick the key from map entry K, otherwise we pick the value.
Therefore, to generate all possible permutations you need to generate all possible boolean arrays (same as binary numbers) of length N and then use each one to create a corresponding string.

Related

How to refactor this Java code so it creates a String List of key:value pairs as opposed to printing them out?

So I saw some code online that I implemented into my program to sort a hashmap<String, int> based on the values of the integers, and it works successfully but only outputs the values, I need to store the values into a String list but don't understand the code well enough to do this and have been failing repeatedly, it is confusing me to have so many brackets.
If anyone could point me in the right direction that'd be helpful, I don't even know what to google to find out how to do this, thanks.
public List<String> getMaxList(int n, HashMap<String, Integer> itemCount){
List<Map.Entry<String, Integer>> maxList = new ArrayList<>();
Object[] a = itemCount.entrySet().toArray();
int n_iterator = n-1;
Arrays.sort(a, new Comparator() {
public int compare(Object o1, Object o2) {
return ((Map.Entry<String, Integer>) o2).getValue()
.compareTo(((Map.Entry<String, Integer>) o1).getValue());
}
});
for (Object e : a) {
System.out.println(((Map.Entry<String, Integer>) e).getKey() + " : "
+ ((Map.Entry<String, Integer>) e).getValue());
if (n_iterator <= 0){
break;
} else {
n_iterator--;
}
}
return null;
}
}
How to refactor this Java code so it creates a String List of key:value pairs as opposed to printing them out?
Try it like this.
Create some data
Map<String, Integer> map =
Map.of("A", 10, "B", 3, "C", 8, "D", 2);
Now stream the map's entry set. Sort based on the value and convert the entry to a String:int string and put in a list.
List<String> list = map.entrySet().stream()
.sorted(Entry.comparingByValue()))
.map(e -> e.getKey() + ":" + e.getValue())
.collect(Collectors.toList());
System.out.println(list);
Prints
[D:2, B:3, C:8, A:10]
To sort in descending order, change the above sort method to
.sorted(Entry.<String,Integer>comparingByValue().reversed())
Note that the Entry comparator had to be witnessed with the types as the compiler could not discern them to properly apply the reversed() method. The above simply reverses the natural sorting order for Integers which is ascending.
Finally, not all objects provide methods that return comparators, so the following would also have worked.
.sorted(Comparator.comparing(Entry::getValue,
Comparator.reverseOrder()))

Java HashMap - How to simultaneously get and then remove a random entry from a HashMap?

I was wondering if it is possible to get a random value from a HashMap and then straight after remove that key/value from the HashMap? I can't seem to find any method that works, would a different data structure be more appropriate for this?
Edit:
I should've been more clear, I generate a random number and then retrieve the value that corresponds with that random number. I need to return the value and then remove the entry from the map.
Maybe Map#computeIfPresent would work in your case. From its documentation:
If the value for the specified key is present and non-null, attempts to compute a new mapping given the key and its current mapped value.
If the remapping function returns null, the mapping is removed.
var map = new HashMap<Integer, String>();
map.put(1, "One");
map.put(2, "Two");
map.put(3, "Three");
map.computeIfPresent(2, (k, v) -> {
// `v` is equal to "Two"
return null; // Returning `null` removes the entry from the map.
});
System.out.println(map);
The above code outputs the following:
{1=One, 3=Three}
If you were to use a ConcurrentHashMap, then this would be an atomic operation.
The best way to both return and remove the key-value pair from a HashMap is by using the remove(key) method. This method removes the entry associated with the key and returns its corresponding value.
Integer randomNumber = new Random().nextInt(10);
Map<Integer, String> map = new HashMap<>();
String valueOfRandomNumberKey = map.remove(randomNumber);
The problem, as I understand it, is this: given a HashMap you want to
Choose a key at random from among the the keys currently associated in the Map;
Remove that association of that randomly chosen key from the map; and
Return the value that had, until recently, been associated with that key
Here's an example of how to do this, along with some a little test/demonstration routine:
public class Main
{
private static <K, V> V removeRandomEntry(Map<K, V> map){
Set<K> keySet = map.keySet();
List<K> keyList = new ArrayList<>(keySet);
K keyToRemove = keyList.get((int)(Math.random()*keyList.size()));
return map.remove(keyToRemove);
}
public static void main(String[] args){
Map<String, String> map = new HashMap<>();
for(int i = 0; i < 100; ++i)
map.put("Key" + i, "Value"+i);
int pass = 0;
while (!map.isEmpty())
System.out.println("Pass " + (++pass) + ": Removed: " + removeRandomEntry(map));
}
}
I would do it like this:
Hashmap<Integer, Object> example;
int randomNum = ThreadLocalRandom.current().nextInt(0, example.size());
example.getValue() //do something
example.remove(new Integer(randomNum));

How to count occurrences for each value in MultiMap ? (java)

I have Multimap, which contains two strings. Example:
1 = [key,car],
2 = [key,blue],
3 = [key,car]
Multimap definition (I am using Guava library):
ListMultimap<Integer, String> map_multi = ArrayListMultimap.create();
And this is how I put values in MultiMap:
for (int i = 0; i < list.size(); i++) {
if (i + 1 < list.size()) {
multimap.put(i,(String) list.get(i));
multimap.put(i,(String) list.get(i+1));
} else if (i + 1 == list.size()) {
}
}
I want to count the occurrences of the same value inside the multimap.
So the result should be 2 if i count how many values [key,car] are (per example I have given above) in my multimap:
occurrences of [key,car] = 2
occurrences of [key,blue] = 1
I have also tried to implement this with multi value HashMap and I was counting it with this, way (Storage is class where I store two string values inside object):
B = Collections.frequency(new ArrayList<Storage>(map.values()), map.get(number));
But I don't get the right results.
You can achieve what you want by creating a map that has your multimap values as the keys and the count as the value:
Map<Collection<String>, Long> result = map_multi.asMap().values().stream()
.collect(Collectors.groupingBy(v -> v, Collectors.counting()));
Here I've used Guava's Multimap.asMap method to get a view over the original multimap, then collected the values into a new map.
Another way, without streams:
Map<Collection<String>, Integer> result = new HashMap<>();
map_multi.asMap().values().forEach(v -> result.merge(v, 1, Integer::sum));
This uses the Map.merge method to accumulate equal values by counting its occurrences.
Please try this code. map_multi.get(key).size() is your answer.
ListMultimap<Integer, String> map_multi = ArrayListMultimap.create();
map_multi.put(1, "car");
map_multi.put(2, "blue");
map_multi.put(3, "apple");
map_multi.put(1, "car");
for (Integer key : map_multi.keySet()) {
System.out.println(map_multi.get(key).get(0) + " occurances: " + map_multi.get(key).size());
}
Output:
car occurances: 2
blue occurances: 1
apple occurances: 1
So the first step is to create a Map<Integer, List<String>> from your ListMultimap. You can do this in the following way:
Map<Integer, List<String>> collect = map_multi.entries()
.stream()
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue,
Collectors.toList())));
Then let's say you have a List<String> with car and key in it.
Example:
List<String> myList = List.of("key", "car"); // java 9
You just iterate through the values() of the map and check if myList contains all elements from map's lists.
long count = collect.values()
.stream()
.filter(list -> list.containsAll(myList))
.count();
I think you're using wrong collection to store your data. Based on what you wrote, you want a map with integer keys and two-element tuples as values and then use Multiset to count frequencies:
Multiset is a collection that supports order-independent equality, like Set, but may have duplicate elements. A multiset is also sometimes called a bag.
Elements of a multiset that are equal to one another are referred to as occurrences of the same single element. The total number of occurrences of an element in a multiset is called the count of that element (the terms "frequency" and "multiplicity" are equivalent, but not used in this API).
The code below assumes you have proper implementation of two-element tuple (aka pair, or your Storage class, but with proper equals and hashCode implementations), like this one from jOOL:
HashMap<Integer, Tuple2<String, String>> m = new HashMap<>();
Tuple2<String, String> carTuple = new Tuple2<>("key", "car");
Tuple2<String, String> blueTuple = new Tuple2<>("key", "blue");
m.put(1, carTuple);
m.put(2, blueTuple);
m.put(3, carTuple);
ImmutableMultiset<Tuple2<String, String>> occurrences =
ImmutableMultiset.copyOf(m.values());
System.out.println(occurrences); // [(key, car) x 2, (key, blue)]
If you need to have few values (tuples) mapped under one key (integer), then you should change the first line to multimap:
ListMultimap<Integer, Tuple2<String, String>> m = ArrayListMultimap.create();
so that m.put(1, anotherTuple) is possible and putting doesn't override first value (carTuple) but rather adds it under 1 values list.
EDIT:
You can implement Tuple2 yourself if you don't need/want additional dependency, it could look like this class:
public class Tuple2<T1, T2> {
public final T1 v1;
public final T2 v2;
public Tuple2(T1 v1, T2 v2) {
this.v1 = v1;
this.v2 = v2;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Tuple2)) {
return false;
}
#SuppressWarnings({"unchecked", "rawtypes"}) final Tuple2<T1, T2> that = (Tuple2) o;
return Objects.equals(v1, that.v1) && Objects.equals(v2, that.v2);
}
#Override
public int hashCode() {
return Objects.hash(v1, v2);
}
#Override
public String toString() {
return "(" + v1 + ", " + v2 + ")";
}
}

Split 1 ArrayList into multiple ones based on condition

I have a big ArrayList containing strings. I want to split it, based on a condition that an element meets. For example, it could be the string length if ArrayList contained String. What is the most efficient (not the easiest) way to do that ?
['a', 'bc', 'defe', 'dsa', 'bb']
after would result to :
['a'], ['bc', 'bb'], ['dsa'], ['defe']
It's easy and fairly efficient to do it using Java 8 streams:
Collection<List<String>> output = input.stream()
.collect(Collectors.groupingBy(String::length))
.values();
If you run it with this input:
List<String> input = Arrays.asList("a", "bc", "defe", "dsa", "bb");
You will get this output:
[[a], [bc, bb], [dsa], [defe]]
A non-stream version would do the same thing, i.e. build a Map<K, List<V>> where V is your value type (e.g. String in your case), and K is the type of the grouping value (e.g. Integer for the length).
Doing it yourself (like shown in answer by palako) might be slightly more efficient at runtime, but likely not in any way that matters.
Staying with Java 8, that would be this:
Map<Integer, List<String>> map = new HashMap<>();
for (String value : input)
map.computeIfAbsent(value.length(), ArrayList::new).add(value);
Collection<List<String>> output = map.values();
For earlier versions of Java, you can't use computeIfAbsent(), so:
Map<Integer, List<String>> map = new HashMap<Integer, List<String>>();
for (String value : input) {
Integer length = Integer.valueOf(value.length()); // box only once
List<String> list = map.get(length);
if (list == null)
map.put(length, list = new ArrayList<String>());
list.add(value);
}
Collection<List<String>> output = map.values();
The most efficient way is to iterate the original list just once. What you do is you create buckets and add to those buckets.
public class Q1 {
public static void main(String[] args) {
String[] original = {"a","bc","defe","dsa","bb"};
List<String> originalValues = new ArrayList<String>(Arrays.asList(original));
Map<Integer, List<String>> orderedValues = new HashMap<Integer, List<String>>();
Iterator<String> it = originalValues.iterator();
while (it.hasNext()) {
String currentElement = it.next();
int length = currentElement.length();
if(!orderedValues.containsKey(length)) {
orderedValues.put(length, new ArrayList<String>());
}
orderedValues.get(length).add(currentElement);
}
System.out.println(orderedValues.values());
}
}
You might be tempted to use an array of arrays instead of a Map, and use the size of the string as the index to the array position, but then you need to watch out for a case where you don't have strings of a certain length. Imagine a case where you only have a string in the original list, but it has 100 characters. You would have 99 empty positions and one string in the array at position 100.

how to shuffle key-value pairs?

I have a set of values which need to be shuffled when needed.
I don't know which variable type is best for me. Data is actually based on key-value structure.Like;
100 "white"
200 "black"
300 "red"
and like that. What I want to do is to change the key-value pairs according to I don't know yet, some algorithm.But they need to be shuffled like this, but shuffling need to be not random, so I can revert data when I need.
100 "red"
200 "white"
300 "black"
I don't really know how my approach should be to the solution. Should I use HashTable or something, and how can I shuffle them dynamically?
Any help is appreciated
Another way for shuffling the key-value mappings randomly:
public static <K,V> void shuffleMap(Map<K,V> map) {
List<V> valueList = new ArrayList<V>(map.values());
Collections.shuffle(valueList);
Iterator<V> valueIt = valueList.iterator();
for(Map.Entry<K,V> e : map.entrySet()) {
e.setValue(valueIt.next());
}
}
Edit:
If you don't want to change the original map (since you need it afterwards), you can create a new one instead:
public static <K,V> Map<K,V> shuffleMap(Map<K,V> map) {
List<V> valueList = new ArrayList<V>(map.values());
Collections.shuffle(valueList);
Iterator<V> valueIt = valueList.iterator();
Map<K,V> newMap = new HashMap<K,V>(map.size());
for(K key : map.keySet()) {
newMap.put(key, valueIt.next());
}
return newMap;
}
You do not really want a seemingly-randomly mixing which can be reverted (which quickly gets complicated), but simply retain your original map. If this does not fit, you need to describe your problem better.
Okay, you want to encrypt the mapping by using a secret key, giving another mapping, and then decrypt it again. Obviously random shuffling does not help here, and even pseudorandom is no good, since it gives no reliable way to reshuffle. In the basic case, your key would be a invertible map between the keys of our mapping.
public static <K,V> Map<K,V> encryptMap(Map<K,V> plainMap, Map<K,K> key) {
Map<K,V> cryptoMap = new HashMap<K,V>(plainMap.size());
for(Map.Entry<K,V> entry : plainMap.entrySet()) {
cryptoMap.put(key.get(entry.getKey()), entry.getValue());
}
return cryptoMap;
}
Decryption works the same, in fact, only using the reverse map of the key.
So, when you have your example keys of {100, 200, 300}, any permutation of these keys is a valid key for our "encryption scheme".
(There are only 6 possible ones, which is not very secure.)
Map sampleKey = new HashMap<Integer, Integer>();
sampleKey.put(100, 200);
sampleKey.put(200, 300);
sampleKey.put(300, 100);
Map sampleUnKey = new HashMap<Integer, Integer>();
for(Map.Entry<Integer, Integer> e : sampleKey) {
sampleUnKey.put(e.getValue(), e.getKey());
}
Map<Integer, String> data = new HashMap<Integer, String>();
data.put(100, "white");
data.put(200, "black");
data.put(300, "red");
System.out.println(data);
Map<Integer, String> encrypted = encryptMap(data, sampleKey);
System.out.println(encrypted);
Map<Integer, String> decrypted = encryptMap(data, sampleUnKey);
System.out.println(decrypted);
The map decrypted now should be the same as the original map.
For bigger keysets you would want to find a scheme to get a suitable
permutation of keys from some input-able key.
It looks like you need a list of tupples. A Map is exactly that. However, a standard like HashMap has no functionality for changing the relationship between key and value.
I think I would have implemented my own Map for this. Create a class that implements java.util.Map, implement the required methods and create some other methods for "mixing".
It all dependes on what functionality you really need on the list of tupples. Do you need to look up colors very fast? Can there be more than one tupple with the same numbers?
I am not sure how exactly you are going to shuffle the pairs, but if you need to shuffle them based on the key, you can use a Map:
Map<String, String> map = new HashMap<String, String>();
map.put("100", "white");
map.put("200", "black");
map.put("300", "red");
// swap 100 with 200
String temp = map.get("100");
map.put("100", map.get("200"));
map.put("200", temp);
Alternatively, if you need to shuffle the pair randomly, you can create a class Pair (which will basically store an int and a String), as suggested by larsmans, and store them in an array. Then, a slightly-modified version of Fisher-Yates shuffle can be used. Something along these lines:
// initialize list
List<Pair<Integer, String>> values = new ArrayList<Pair<Integer, String>>();
values.add(new Pair<Integer, String>(100, "white"));
values.add(new Pair<Integer, String>(200, "black"));
values.add(new Pair<Integer, String>(300, "red"));
// shuffle
System.out.println(values); // e.g., [100 white, 200 black, 300 red]
Random random = new Random();
for (int i = values.size() - 1; i > 1; i--) {
int j = random.nextInt(i + 1);
// swap values between i-th Pair and j-th Pair
Pair<Integer, String> iPair = values.get(i); // the iPair :-)
Pair<Integer, String> jPair = values.get(j);
String iString = iPair.getSecond();
iPair.setSecond(jPair.getSecond());
jPair.setSecond(iString);
}
System.out.println(values); // e.g., [100 red, 200 black, 300 white]

Categories

Resources