Randomly select n items from a map - java

I am trying to randomly generate 'n' number of items from a HashMap where 'n' is determined by the user.
Here is what I have so far:
public static void main(String []args){
int numColors = 3;
HashMap<String, String> map = new HashMap<String, String>();
map.put("White","FFFFFF");
map.put("Blank","000000");
map.put("Red","ED0A15");
map.put("Green","06F76C");
map.put("Blue","0689FF");
map.put("Sky Blue","00C2FC");
map.put("Light Blue","08F0FC");
map.put("Silver","C0BFC5");
map.put("Mint","ABD3CA");
map.put("Off White","FFEFF0");
map.put("Purple","736FFA");
map.put("Lavendar","DEBEEF");
map.put("Hot Pink","F5159A");
map.put("Pink","DB39CC");
map.put("Light Pink","F5C2E3");
map.put("Blush","C95FA7");
map.put("Orange","D4361B");
map.put("Yellow","DEF231");
map.put("Warm White","F3E4C3");
map.put("Turquoise","01DCA4");
List<String> valuesList = new ArrayList<String>(map.values());
int randomIndex = new Random().nextInt(valuesList.size());
String randomValue = valuesList.get(randomIndex);
System.out.printf(randomValue);
}
It prints 1 random color for me (in hex) which I want, however I am unsure of how/which loop to use in order to generate say 3 random hex colors from the map. I declared numColors as 3 just to try and test this out.
Here is what I ended up going with:
public static void main(String []args){
int numColors = 3;
HashMap<String, String> map = new HashMap<String, String>();
map.put("White","FFFFFF");
map.put("Blank","000000");
map.put("Red","ED0A15");
map.put("Green","06F76C");
map.put("Blue","0689FF");
map.put("Sky Blue","00C2FC");
map.put("Light Blue","08F0FC");
map.put("Silver","C0BFC5");
map.put("Mint","ABD3CA");
map.put("Off White","FFEFF0");
map.put("Purple","736FFA");
map.put("Lavendar","DEBEEF");
map.put("Hot Pink","F5159A");
map.put("Pink","DB39CC");
map.put("Light Pink","F5C2E3");
map.put("Blush","C95FA7");
map.put("Orange","D4361B");
map.put("Yellow","DEF231");
map.put("Warm White","F3E4C3");
map.put("Turquoise","01DCA4");
List<String> keys = new ArrayList<String>(map.keySet());
Random rand = new Random();
for (int i = 0; i < numColors; i++) {
String key = keys.get(rand.nextInt(keys.size()));
System.out.println(map.get(key));
}
}

A simple solution is to shuffle the entire map using Collections.shuffle(map). Then just iterating over it and picking the first n elements.
Of course this doesn't make sense if the map is huge and you only need a couple of elements.
Edit:
Naturally, with this solution you won't get any duplicate entries

If I understand your question, you could do it with
List<String> keys = new ArrayList<String>(map.keySet());
Random rand = new Random();
for (int i = 0; i < numColors; i++) {
String key = keys.get(rand.nextInt(keys.size()));
System.out.println(map.get(key));
}

Changes are mentioned in comments
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
int numColors = 3;
HashMap<String, String> map = new HashMap<String, String>();
map.put("White", "FFFFFF");
map.put("Blank", "000000");
map.put("Red", "ED0A15");
map.put("Green", "06F76C");
map.put("Blue", "0689FF");
map.put("Sky Blue", "00C2FC");
map.put("Light Blue", "08F0FC");
map.put("Silver", "C0BFC5");
map.put("Mint", "ABD3CA");
map.put("Off White", "FFEFF0");
map.put("Purple", "736FFA");
map.put("Lavendar", "DEBEEF");
map.put("Hot Pink", "F5159A");
map.put("Pink", "DB39CC");
map.put("Light Pink", "F5C2E3");
map.put("Blush", "C95FA7");
map.put("Orange", "D4361B");
map.put("Yellow", "DEF231");
map.put("Warm White", "F3E4C3");
map.put("Turquoise", "01DCA4");
// scanner for accepting values
Scanner scan = new Scanner(System.in);
System.out.println("Enter number");
int N = scan.nextInt();
// random object for generating random values
Random rand = new Random();
// converting map values to list
List<String> valuesList = new ArrayList<String>(map.values());
for (int i = 1; i <= N; i++) {
// choose random value
int randomIndex = rand.nextInt(valuesList.size());
// get value
String randomValue = valuesList.get(randomIndex);
// printing
System.out.println("Random value " + i + " : " + randomValue);
}
}
}
To prevent duplicates you can do something like this :
// random object for generating random values
Random rand = new Random();
// converting map values to list
List<String> valuesList = new ArrayList<String>(map.values());
Set<String> set = new HashSet<String>();
while (set.size() != N) {
int randomIndex = rand.nextInt(valuesList.size());
String randomValue = valuesList.get(randomIndex);
set.add(randomValue);
}
System.out.println(set);

As Malt suggested, to prevent duplicates and keep code clean:
List<String> list = new ArrayList(map.values() );
Collections.shuffle(list);

Related

Random strange behavior

I've created just for fun this method to find out how long it takes until this random method goes into a loop and what the seed is when this occurs:
public static void main(String[] args) {
HashMap<Integer,Integer> map = new HashMap<>();
for (int i = 0; i < 1000; i++) {
int key = firstRepeatingSeed();
Integer integer = map.get(key);
map.put(key, integer == null ? 1 : integer+1);
}
System.out.println(map);
}
}
public static int firstRepeatingSeed(){
Random random = new Random();
HashSet<Integer> loopDetector = new HashSet<>();
while (true) {
int gen = random.nextInt();
random.setSeed(gen);
if (!loopDetector.add(gen))
return gen;
}
}
These are the results:
-1381274646=2,
-1686548232=2,
154051178=717,
-381624123=1,
-334394727=1,
-1261880000=1,
-1128634575=1,
658182704=1,
364776881=141,
-1985197764=11,
1266282155=13,
-1108864769=1,
266843583=9,
-1939453764=2,
349725186=4,
1525333558=2,
-106280330=1,
-1865148662=1,
-296326218=1,
-84817968=2,
332765684=1,
-1181949977=1,
-595175830=59,
-206288251=1,
-2043038133=1,
1220626100=1,
-541517940=1,
1373871195=14,
1890953100=3,
-1529367891=1,
-1022666507=3
My question is why does 154051178 and 364776881 show up so often?

a set indexed objects in ArrayList - is it possible?

Is it possible to create a set indexed objects in ArrayList?
I want to create an array of objects - Portal class - and have them indexed in array which size will be defined by user.
import java.util.ArrayList;
import java.util.Scanner;
public class GameFunctions
{
Scanner sc = new Scanner(System.in);
private int portalsQty;
private String[] portalNamesDB = {"name1", "name2", "name3", "name4", "name5"};
ArrayList<Portal> portals = new ArrayList<>();
void setPortalsQty(int portalsQty)
{
this.portalsQty = portalsQty;
}
int getPortalsQty(int portalsQty)
{
return portalsQty;
}
private void createPortals()
{
System.out.println("type the
amount of portals");
portalsQty = sc.nextInt();
System.out.println("number of portals: " + portals.size());
for (int i = 0; i < portalsQty; i++)
{
portals.add(i,p[i]); // CANNOT HAVE VALUES INDEXED LIKE p[i] IN ARRAYLIST
}
}
private void namePortals()
{
int randomNo = (int)(Math.random()*portalsQty);
for (int i = 0; i < portalsQty; i++)
{
System.out.println("Random: " + randomNo);
portals[i].setPortalName(portalNamesDB[randomNo]);
}
}
public void launchGame()
{
createPortals();
namePortals();
}
}
Defining the size of array by user makes using tables not feasible, as we encounter NullPointerException.
Is there any other solution to make dynamic size of the table and have the elements indexed?
import java.util.HashMap;
HashMap<Integer, portal>portals = new HashMap<>();
System.out.println("number of portals: " + portals.size());
for (int i = 0; i < portalsQty; i++)
{
int randomNo = (int)(Math.random()*portalsQty);
portals.put(portalNamesDB[randomNo], i);
}
Mureinik and chrylis are right, a map, or HashMap would probably work best here.
I added an example of how you could implement it. This way you are giving each portal a name and quantity value all in one for loop. The portal name is the key, and the quantity number is the value in my example.
I hope that helps!
You could emulate this behavior with a map that maps from the index to the object:
Map<Integer, Portal> indexes = new HashMap<>();

Copy Two Dimensional ArrayList as new

So the issue I'm having is after copying the 2d arraylist, changing the element from one 2d arraylist affects the other 2d arraylist. I want them to be completely separate in memory.
First example shows how it works correctly with 1d arraylists...
import java.util.ArrayList;
public class QuickTest {
public static void main(String[] args) {
ArrayList<Integer> firstList = new ArrayList<>();
ArrayList<Integer> secondList = new ArrayList<>();
Integer counter = 2;
for(int arrI = 0; arrI < 4; arrI++, counter+=2){
firstList.add(counter);
}
secondList = new ArrayList<>(firstList);
System.out.println("firstList.get(2) = " + firstList.get(2));
System.out.println("secondList.get(2) = " + secondList.get(2));
firstList.set(2, 7);
System.out.println("firstList.get(2) = " + firstList.get(2));
System.out.println("secondList.get(2) = " + secondList.get(2));
}
}
Expected output:
Notice how the element from the first arraylist is changed but not the second arraylist element is not changed. This is good and what we want.
Now to try and copy the 2d arraylists...
import java.util.ArrayList;
public class QuickTest {
public static void main(String[] args) {
ArrayList<ArrayList<Integer>> firstTwoDimList = new ArrayList<>();
ArrayList<ArrayList<Integer>> secondTwoDimList = new ArrayList<>();
firstTwoDimList.add(new ArrayList<Integer>());
firstTwoDimList.add(new ArrayList<Integer>());
firstTwoDimList.add(new ArrayList<Integer>());
Integer counter = 2;
for(int arrI = 0; arrI < firstTwoDimList.size(); arrI++, counter+=2){
firstTwoDimList.get(arrI).add(counter);
counter+=2;
firstTwoDimList.get(arrI).add(counter);
}
secondTwoDimList = new ArrayList<>(firstTwoDimList);
System.out.println("firstTwoDimList.get(1).get(0) = " + firstTwoDimList.get(1).get(0));
System.out.println("secondTwoDimList.get(1).get(0) = " + secondTwoDimList.get(1).get(0));
firstTwoDimList.get(1).set(0, 7);
System.out.println("firstTwoDimList.get(1).get(0) = " + firstTwoDimList.get(1).get(0));
System.out.println("secondTwoDimList.get(1).get(0) = " + secondTwoDimList.get(1).get(0));
}
}
Unexpected output:
Anyone have any idea what the reason for this is, and what the best solution would be?
This is what is happening in the 1D array list case, in terms of references:
This is what is happening in the 2D array list case:
This means that when you copy an array list using this:
new ArrayList<>(someOldArrayList)
the items themselves don't get copied, only a new array list object is created, referring to all the items in the old array list.
In the second case, you are only changing what array list 2's items are, but index 1 of first list and second list refers to the same array list 2.
To fix this, you need to copy the array lists inside first list and second list as well. One way to do this:
secondList = new ArrayList<>(firstList.stream().map(x -> new ArrayList<>(x)).collect(Collectors.toList()));
You should iterate through the size of the first dimension of the firstTwoDimArray and add new reference of each second dimension to the secondTwoDimArray. i.e.
for(int index = 0; index < firstTwoDimList.size(); index++) {
secondTwoDimList.add(new ArrayList<Integer>(firstTwoDimList.get(index)));
}
The difference between your first and second example is that in the second one you use get(). This get() returns a new variable, so you assign the integers to it and not to the original ArrayList.
If you want to assign a value:
firstTwoDimList.set(1, new ArrayList<Integer>(Arrays.asList(0, 7)));
I guess I was looking for something like this...
import java.util.ArrayList;
public class QuickTest {
public static ArrayList<ArrayList<Integer>> getTwoDimArrListCopy(ArrayList<ArrayList<Integer>> original){
ArrayList<ArrayList<Integer>> copy = new ArrayList<>();
for (ArrayList<Integer> arr: original){
copy.add(new ArrayList<Integer>(arr));
}
return copy;
}
public static void main(String[] args) {
ArrayList<ArrayList<Integer>> firstTwoDimList = new ArrayList<>();
ArrayList<ArrayList<Integer>> secondTwoDimList = new ArrayList<>();
firstTwoDimList.add(new ArrayList<Integer>());
firstTwoDimList.add(new ArrayList<Integer>());
firstTwoDimList.add(new ArrayList<Integer>());
Integer counter = 2;
for(int arrI = 0; arrI < firstTwoDimList.size(); arrI++, counter+=2){
firstTwoDimList.get(arrI).add(counter);
counter+=2;
firstTwoDimList.get(arrI).add(counter);
}
secondTwoDimList = getTwoDimArrListCopy(firstTwoDimList);
System.out.println("firstTwoDimList.get(1).get(0) = " + firstTwoDimList.get(1).get(0));
System.out.println("secondTwoDimList.get(1).get(0) = " + secondTwoDimList.get(1).get(0));
firstTwoDimList.get(1).set(0, 7);
System.out.println("firstTwoDimList.get(1).get(0) = " + firstTwoDimList.get(1).get(0));
System.out.println("secondTwoDimList.get(1).get(0) = " + secondTwoDimList.get(1).get(0));
}
}
I was just hoping there was a built in library that would do that getTwoDimArrListCopy() function for me...

Subtracting values in 2 different list in android

I have a integer array of years and its corresponding values it looks like the below code
int[] earningYear = {2012,2013,2014};
int[] earningAmount = {100,150,120};
int[] expenseYear = {2012,2014};
int[] expenseAmount = {50,30};
i want to subtract expenseAmount from earningsAmount corresponding to year and the output list should look like this
int[] savingsYear = {2012,2013,2014};
int[] savingsamount = {50,150,90};
please help me in doing this. Thanks in advance
Answers given by #umair.ali and #Casper are good, converting your arrays to Maps is the way to go.
I would suggest using TreeMap (or other implementation of SortMap interface). TreeMap sorts its keys in ascending order by default. That way, if you really want to have plain array of savings as result, you won't have to do any awkward map sorting after processing.
Here's complete example:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
public class SavingsApp {
public Map<Integer, Integer> arraysToMap(int[] years, int[] money) {
Map<Integer, Integer> newMap = new HashMap<Integer, Integer>();
if (years == null || money == null || years.length != money.length) {
throw new IllegalArgumentException();
}
for (int i=0; i< years.length; i++ ) {
newMap.put(years[i], money[i]);
}
return newMap;
}
public Map<Integer, Integer> calculateSavings(Map<Integer, Integer> earningsMap, Map<Integer, Integer>expensesMap) {
Map<Integer, Integer> savingsMap = new TreeMap<Integer, Integer>();
savingsMap.putAll(earningsMap);
for (Entry<Integer, Integer> expensePerYear : expensesMap.entrySet()) {
Integer year = expensePerYear.getKey();
Integer expense = expensePerYear.getValue();
Integer earning = savingsMap.get(year);
if (earning == null) {
earning = 0;
}
savingsMap.put(year, earning-expense);
}
return savingsMap;
}
public static void main(String[] args) {
int[] earningYear = {2012,2013,2014};
int[] earningAmount = {100,150,120};
int[] expenseYear = {2012,2014};
int[] expenseAmount = {50,30};
SavingsApp app = new SavingsApp();
// convert arrays to maps
Map<Integer, Integer> earningsMap = app.arraysToMap(earningYear, earningAmount);
Map<Integer, Integer> expensesMap = app.arraysToMap(expenseYear, expenseAmount);
// compute savings per year
Map<Integer, Integer> savingsMap = app.calculateSavings(earningsMap, expensesMap);
// convert result map to array
List<Integer> savingsList = new ArrayList<Integer>(savingsMap.values());
Integer[] savingsArray = new Integer[savingsList.size()];
savingsList.toArray(savingsArray);
}
}
One thing to notice is that savingsArray will be of Integer[] type instead of int[].
If earningYear always contains all years and both earningYear and expenseYear are sorted, then this would be a possible solution
int[] earningYear = {2012,2013,2014};
int[] earningAmount = {100,150,120};
int[] expenseYear = {2012,2014};
int[] expenseAmount = {50,30};
int[] savingsYear = new int[earningYear.length()];
int[] savingsAmount = new int[earningYear.length()];
int expenseYearIndex = 0;
for(int i = 0; i < earningYear.length(); i++) {
savingsYear[i] = earningYear[i];
savingsAmount[i] = earningsAmount[i];
if(expenseYear.length() > 0 && expenseYear[expenseYearIndex] == earningYear[i]) {
savingsAmount[i] -= expenseYear[expenseYearIndex];
expenseYearIndex++;
}
}
This should do it.
However a better solution would be something with a List, already suggested, and some objects holding the year and the amount for the year.

Joining values in an arraylist

Say I have an arraylist a with the values:
a[0] = G
a[1] = B
a[2] = D
I was wondering, how can I create a new arraylist in java, that joins index values at random places and puts it into a new arraylist b
So like:
b[0] = GB
b[1] = D
or
b[0] = G
b[1] = BD
or
b[0] = GBD
The order of the values is kept the same, it's just the different combinations of the joins, and at different places over different amounts.
Something like (pseudocode)
newA = new ArrayList<String>();
for (b : a) {
if (Math.random() > 0.5) newA.add(b);
else newA.set(previous, newA.get(previous) + b);
}
Assuming you can get the "random position" yourself, my thought would be to use something like the following:
import org.apache.commons.lang.StringUtils;
public List<String> mergeAt(ArrayList<String> input, int offset) {
List<String> result = new ArrayList<String>();
result.add(StringUtils.join(input.subList(0, offset), '');
result.add(StringUtils.join(input.subList(offset, input.size()), '');
return result;
}
First merge element at index i with index j:
yourList.set(i, yourList.get(i) + yourList.get(j));
Then remove element at index j:
yourList.remove(j);
This satisfies all the conditions you listed above. you can adjust joinIndex and joinSize to whatever you want and it will start at the joinIndex position and concatenate joinSize characters.
Fixed the issue with the exceeding bounds. Now it will just concat as many characters at the end as exist.
import java.util.ArrayList;
import java.util.List;
public class TestJoiner
{
public static void main(String[] args){
List<String> list = new ArrayList<String>();
list.add("G");
list.add("B");
list.add("D");
list.add("L");
list.add("G");
list.add("A");
//Get Random Int
int joinIndex=3;
int joinSize=2;
//check join size, make sure it doesn't exceed bounds.
joinSize=(joinIndex+joinSize+1)>list.size()?list.size()-joinIndex-1:joinSize;
//join
for(int a=joinIndex;a<joinIndex+joinSize;a++){
list.set(joinIndex,list.get(joinIndex)+list.get(a+1));
}
//shift
for(int c=joinIndex+1;c<list.size()-joinSize;c++){
list.set(c,list.get(c+joinSize));
}
//Truncate
list=list.subList(0,list.size()-joinSize);
System.out.println(list);
}
}
badger. Here is task solution. Just use List<String> Shaker.shake(List<String>) method.
Shaker class:
import java.util.List;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Random;
public class Shaker {
public static List<String> shake(List<String> sourceList) {
Random random = new Random();
// We'll use cloned original list
LinkedList<String> itemsList = new LinkedList<String>(sourceList);
// Count how much items need shaking
int itemsToMerge = itemsList.size();
// New generated list
List<String> newList = new ArrayList<String>();
// Temporary values, used in cycle
int firstGroupItemIndex = 0;
while (0 < itemsToMerge) {
// Select random number of merged items
int groupLength = random.nextInt(itemsToMerge) + 1;
// Create inserted string value
StringBuilder insertedValue = new StringBuilder();
int lastGroupItemIndex = firstGroupItemIndex + groupLength;
for (int i = firstGroupItemIndex; i < lastGroupItemIndex; i++) {
insertedValue.append(itemsList.removeFirst());
}
// Add merged string value
newList.add(insertedValue.toString());
firstGroupItemIndex = lastGroupItemIndex;
itemsToMerge -= groupLength;
}
return newList;
}
}
And Test class:
import java.util.List;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
List<String> originalList = new ArrayList<String>();
originalList.add("G");
originalList.add("B");
originalList.add("C");
originalList.add("L");
originalList.add("G");
originalList.add("A");
List<String> newList = Shaker.shake(originalList);
System.out.println("Original list: " + originalList);
System.out.println("Shaked list: " + newList);
}
}
Look at the result:
Original list: [G, B, C, L, G, A]
Shaked list: [GBC, LG, A]
If you have questions about the code, I'll answer you with pleasure.
You can always find solution sources at github.com.

Categories

Resources