How to get random objects from a JSON array - java

This JSONAray:
"cows": [
{
"age": 972,
"name": "Betty"
"status": "publish",
"sticky": "pregnant"
},
{
"age": 977,
"name"; "Kate"
"status": "publish",
"sticky": "heat"
},
{
"age": 959,
"name": "Julie"
"site_age": 63178480,
"sticky": "Nursing"
},
...
}
that contains 20 objects. What I wanted is this: get 3 random objects out of the 20. And the ages of any of the three won't be a certain number say 961.
Currently this what I am doing:
private void parseCowsReq(JSONObject array) {
try {
for (int i = 0; i < 3; i++) {
int randumNum = getRandomCow(array);
JSONObject jsonObject = array.getJSONObject(randumNum);
String cowName = jsonObject.getString("name");
String cowStatus = jsonObject.getString("status");
Log.d(TAG, "Cow name is " + cowName + "cow Status is " + cowStatus);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
private int getRandomCow(JSONArray jsonArray) {
int length = jsonArray.length();
int[] array;
array = new int[length-1];
int rnd = new Random().nextInt(array.length);
return array[rnd];
}
There are of issues with this code.
I don't know how to ensure that the object gotten in line
JSONObject jsonObject = array.getJSONObject(randumNum); won't have an
age of 961
The random number gotten is always 0
Please do you have any idea how this can be done?

you can do it with this:
public ArrayList<Integer> getRandomObject(JSONArray jsonArray, int indexesWeeNeed){
Random rn = new Random();
Set<Integer> generated = new LinkedHashSet<>();
while(generated.size() < indexesWeeNeed){
int index = rn.nextInt(10);
JSONObject jsonObject = (JSONObject) jsonArray.get(index);
int age = jsonObject.getInt("age");
if(age<961) {
generated.add(index);
}
}
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.addAll(generated);
return arrayList;
}

One part that's messed up is when you call
Random().nextInt(array.length);
array.length is the new array you just created. You need to perform the function on the existing array:
Random().nextInt(jsonArray)
to get a random number other than zero.
As for ensuring you don't get a certain age, I'd suggest breaking up the code to not call the getRandomCow(array) function inside of the for loop. When you retrieve a cow, check the name doesn't match, check the age, and if it works, keep it. If not get another cow.

Well firstly, load the objects:
JSONArray array = /* your array */;
Next, we need a method to retrieve 3 unique objects from the JSONArray (which is actually a List). Let's shuffle the indexes of the json array, so that we don't end up having to repeatedly generate duplicates:
public Stream<JSONObject> randomObjects(JSONArray array, int amount) {
if (amount > array.size()) {
//error out, null, return array, whatever you desire
return array;
}
List<Integer> indexes = IntStream.range(0, array.size()).collect(Collectors.toList());
//random, but less time generating them and keeping track of duplicates
Collections.shuffle(indexes);
Set<Integer> back = new HashSet<>();
Iterator<Integer> itr = indexes.iterator();
while (back.size() < amount && itr.hasNext()) {
int val = itr.next();
if (array.get(val).getInt("age") != 961) { //or any other predicates
back.add(val);
}
}
return back.stream().map(array::get);
}
Using this, we can select the three objects from the list and utilize them how we wish:
randomObjects(array, 3).map(o -> o.getInt("age")).forEach(System.out::println);
//972
//977
//952
When I said "or any other predicates, you can pass those as well via the method:
public Stream<JSONObject> randomObjects(..., Predicate<Integer> validObject) {
//...
int val = itr.next();
if (validObject.test(val)) {
back.add(val);
}
//...
}
Which would mean you could change the blacklisting per method call:
Stream<JSONObject> random = randomObjects(array, 3, val -> array.get(val).getInt("age") != 961);

Related

(Java) How to Overwrite A 2D Array Where I've Already Declared Null Values

Hi I'm using a 2d array to simulate a vending machine.
I want to declare an array with a set length and make it oversize so it can accommodate me adding items to it.
Ex:
String[][] itemsT1 = new String[][] {null, null, null, null, null, null, null, null, null};
int itemCount = 0;
// The way I've been adding things in my "addItem' method is:
if ( (items.length > 0) && (itemsCount < items.length) ) {
items[itemsCount][0] = nameOfItem;
items[itemsCount][1] = expirationDate;
itemsCount++;
System.out.println("Item added");
}
// so that the end result of the array should be (with nameOfItem = "Water" and expDate = "15") something like this:
{ {"Water", "15"}, null, null, null, null, null, null, null, null}
// and it's scalable to be hypothetically used again:
{ {"Water", "15"}, {"Chocolate", "20"}, {"Juice", "25"}, null, null, null, null, null, null}
I might be back with more questions so thank you for answering, and let me know if I need to provide more!
Declare your 2D String Array and initialize to the number of items you want to handle within your vending machine, let's say 10 Rows (items) with 3 data Columns in each row:
String[][] items = new String[10][3];
Open stream to accept Keyboard input using the Scanner class:
Scanner userInput = new Scanner(System.in);
Now create a for loop to iterate through each item row within the array so that each can be filled by the User. The following is in its' simplest form with no validation. If you just hit the Enter key when asked for an Item Name then the entry process will end:
for (int i = 0; i < items.length; i++) {
System.out.print("Enter a name for item #" + (i + 1) + ": -> ");
String itemName = userInput.nextLine().trim();
if (itemName.isEmpty()) {
break;
}
System.out.print("Enter expiry for item #" + (i + 1) + ": -> ");
String itemExpiry = userInput.nextLine().trim();
System.out.print("Enter price for item #" + (i + 1) + ": -> ");
String itemPrice = userInput.nextLine().trim();
items[i][0] = itemName;
items[i][1] = itemExpiry;
items[i][2] = itemPrice;
System.out.println();
}
Now, display all the entered items contained within the array into the Console Window:
for (String[] diffItems : items) {
if (diffItems[0] != null) {
System.out.println(Arrays.toString(diffItems));
}
}
If you prefer using 2D arrays, at first you'd need to find the first "empty" space in your vending machine -> a null element. (I'll keep it in a method so it's clear and easier to use)
public int getFreeSpaceIndex(final String[][] vendingMachine) {
for (int index = 0; index < vendingMachine.length; index++)
if (vendingMachine[index] == null)
return index;
// meaning there's no empty space, you should use some static field
// to describe this situation to avoid magic numbers in your code
return -1;
}
After you found the element you can insert whatever you want in there
// you've got the itemT1 array that you've created
// and let's say a user inputs the water item down below,
// I simplified it to declaration, cause it's off the topic
final String[] water = new String[] {"Water", "15"};
final int freeSpaceIndex = findFreeSpaceIndex(itemT1);
if (freeSpaceIndex == -1) {
System.err.printf("There's not enough space for the %s item.", Arrays.toString(water));
} else {
itemT1[freeSpaceIndex] = item;
}
Ultimately you can wrap the array into your own data structure and simply make an addItem(String[]) method that will search for an empty space and handle the overflow.
class VendingMachineStock {
private String[][] stock = new String[][] {null, null, null, null, null, null, null, null, null};
// constructors, api...
public boolean hasFreeSpace() {
return getFreeSpaceIndex() != -1;
}
public boolean addItem(final String[] item) {
final int freeSpaceIndex = getFreeSpaceIndex();
if (freeSpaceIndex == -1) {
System.err.printf("There's not enough space for the %s item.", Arrays.toString(water));
return false;
}
stock[freeSpaceIndex] = item;
return true;
}
private int getFreeSpaceIndex() {
for (int index = 0; index < stock.length; index++)
if (stock[index] == null)
return index;
return -1;
}
}
But there're many flaws in a code like this. Anyone can mess up and add String[] {"15", "Water"} or even new String[] {"15", "30", "I'm Foo Bar :)"}.
The right data structure for your problem would actually be a linked list with Item objects (as #DevilsHnd mentioned) and your own limiting structure over it.
class Item {
private final String name;
private int price;
// default getters, setters, consturctor...
}
class VendingMachineStock {
private final LinkedList<Item> stock;
// this will limit your stock list size
private int maxSlots;
// I omit API and constructor intentionally, it's off the
// topic, but you must write it
public void addItem(final Item item) {
// you can either throw exceptions or return boolean
// flag indicating the result, it's up to you.
// in this case I prefer a detailed explanation on why
// the method did not add the item.
if (stock.size() >= maxSlots) {
throw new IllegalStateException("Stock overflow, can't add more items");
}
stock.add(item);
}
}

Is it possible to get distinct values from JSONObject in java

I have a json file that contains 500k Objects and this is its format:
"WORKORDER": [
{
"Attributes": {
"SITEID": {
"content": "BEDFORD"
},
"WONUM": {
"content": "1000"
},
"WOPRIORITY": {
"content": 2
},
"WORKTYPE": {
"content": "CM"
}
}
},
{
"Attributes": {
"SITEID": {
"content": "BEDFORD"
},
"WONUM": {
"content": "1000-10"
},
"WORKTYPE": {
"content": "CM"
}
}
}
Im geting the distinct values like this :
for (int i = 0; i < WORKORDER.length(); i++) {
JSONObject obj = WORKORDER.getJSONObject(i);
JSONObject att = obj.getJSONObject("Attributes");
if( att.has(col)){ // getting col from params in the servlet
JSONObject column = att.getJSONObject(col);
Object colval = column.get("content");
if(!(list.contains(colval))) {
out.println( colval);
list.add(colval);
}
But it takes long time for only 5000 objects !
Is there any way to get the distinct values of any column without parsing the whole Json file, otherwise parsing only the column needed.
You are iterating on a JSON with 500k elements. For each element you check if it was previously added in a List. That means your logic will iterate the list 500k times.
Instead, you should use a HashSet, first, the Set prevent duplicated value. So you just need to set.add(value) but the most interesting is the fact that the instance have a constant complexity value. Since it used buckets to organize the value, it doesn't have to iterate the Set fully.
You can read more about that in amit answer's about How can a HashSet offer constant time add operation?
Note that HashSet gives amortized and average time performance of O(1), not worst case. This means, we can suffer an O(n) operation from time to time.
So, when the bins are too packed up, we just create a new, bigger array, and copy the elements to it.
Note that to use any Hash#### implementation, you need to make sure the instance you store implements hashCode and equals correctly. You can find out more about this in the community post about What issues should be considered when overriding equals and hashCode in Java?.
Now for the solution :
Set<Object> sets = new HashSet<>();
for (int i = 0; i < WORKORDER.length(); i++) {
// ...
Object colval = column.get("content");
if(sets.add(colval)){ //`add` return true if it wasn't present already.
out.println( colval);
}
}
I kept the Object type but this should be correctly typed, at least to be sure that those instance are implementing those methods as needed.
colval being an Object, it is possible it doesn't implements correctly the methods needed so I suggest you parse it correctly. You should use column.getString("content) instead or check the instance type.
To validate this, I have used a method to create a fake JSON:
public static JSONObject createDummyJson(int items) {
JSONObject json = new JSONObject();
JSONArray orders = new JSONArray();
json.put("order", orders);
JSONObject attributes;
JSONObject item;
JSONObject order;
Random rand = new Random();
String[] columns = {"columnA", "columnB", "columnC", "columnD"};
for(int i = 0; i < items; ++i) {
order = new JSONObject();
attributes = new JSONObject();
order.put("Attributes", attributes);
orders.put(order);
for(int j = 0; j < rand.nextInt(1000) % columns.length; ++j) {
item= new JSONObject();
long rValue = rand.nextLong();
item.put("content", j%3 == 0 ? ("" + rValue ) : rValue );
attributes.put(columns[j], item);
}
}
return json;
}
Then ran a basic benchmark for both method and had the following results :
public static void main(String[] args) throws Exception {
final int jsonLength = 500_000;
JSONObject json = createDummyJson(jsonLength);
long start = System.currentTimeMillis();
List<Object> list = parseJson(json);
long end = System.currentTimeMillis();
System.out.format("List - Run in %d ms for %d items and output %d lines%n", end-start, jsonLength, list.size());
start = System.currentTimeMillis();
Set<Object> set = parseJsonSet(json);
end = System.currentTimeMillis();
System.out.format("Set - Run in %d ms for %d items and output %d lines%n", end-start, jsonLength, set.size());
}
public static List<Object> parseJson(JSONObject json) {
String col = "columnC";
JSONArray array = json.getJSONArray("order");
List<Object> list = new ArrayList<>();
for (int i = 0; i < array.length(); i++) {
JSONObject obj = array.getJSONObject(i);
JSONObject att = obj.getJSONObject("Attributes");
if (att.has(col)) { // getting col from params in the servlet
JSONObject column = att.getJSONObject(col);
Object colval = column.get("content");
if (!(list.contains(colval))) {
//System.out.println(colval);
list.add(colval);
}
}
}
return list;
}
public static Set<Object> parseJsonSet(JSONObject json) {
String col = "columnC";
JSONArray array = json.getJSONArray("order");
Set<Object> set = new HashSet<>();
for (int i = 0; i < array.length(); i++) {
JSONObject obj = array.getJSONObject(i);
JSONObject att = obj.getJSONObject("Attributes");
if (att.has(col)) { // getting col from params in the servlet
JSONObject column = att.getJSONObject(col);
Object colval = column.get("content");
if (set.add(colval)) {
//System.out.println(colval);
}
}
}
return set;
}
List - Run in 5993 ms for 500000 items and output 46971 lines
Set - Run in 62 ms for 500000 items and output 46971 lines
I even went to a JSON with 5M row (removed the List that would never end)
Set - Run in 6436 ms for 5000000 items and output 468895 lines
Important, remove the line that print in console every new insertion, it will reduce the execution time a bit.

Java: sort 1d array of objects by full or not

I'm trying to compress an array of objects that will have empty items interspersed with complete items. I want to put all the full elements at the start in the same order they started with, and the empty elements on the end.
The object in question uses a String field, "name", and an int field, "weight". An empty version has "no name" and 0 respectively. So an array of the type the method needs to deal with will contain something like:
Fred | 4
Bob | 3
no name | 0
Gina | 9
no name | 0
Yuki | 7
After feeding through the method, the array should go Fred, Bob, Gina, Yuki, no name, no name.
My thought for step one was to just figure out which were full and which weren't, so I came up with this:
public void consolidate() {
boolean[] fullSlots = new boolean[spaces.length];
// pass 1: find empties
for (int i = 0; i < spaces.length; i++) {
fullSlots[i] = spaces[i].getName().equals("no name");
}
}
spaces is the array of objects, getName() retrieves the name field from the object.
I'm not sure where to go from here. Suggestions?
EDIT: Okay, here's what Infested came up with:
public void consolidate()
{
int numberOfEmpties = 0, spacesLength = spaces.length;
Chicken[] spaces2 = new Chicken[spacesLength];
for(int i = 0; i < spaces.length; i++)
{
spaces2[i] = new Chicken(spaces[i].getName(),
spaces[i].getWeight());
}
// pass 1: find empties
for (int i = 0, j = 0; i < spacesLength; i++)
{
if (spaces2[i].getName().equals("no name") == false)
{
spaces[j] = new Chicken(spaces2[i].getName(),
spaces2[i].getWeight());
j++;
}
else
{
numberOfEmpties++;
}
}
for (int i = spacesLength - 1; numberOfEmpties > 0 ; numberOfEmpties--, i--)
{
spaces[i] = new Chicken("no name", 0);
}
}
Tested and working.
Java's Arrays.sort is stable, meaning that the relative order of equal elements is not going to change.
This sort is guaranteed to be stable: equal elements will not be reordered as a result of the sort.
You can use this property of the sorting algorithm to sort all your elements with a simple comparator:
Arrays.sort(
spaces
, new Comparator() {
public int compare(Object o1, Object o2) {
MyClass a = (MyClass)o1;
MyClass b = (MyClass)o2;
boolean aIsEmpty = "no name".equals(a.getName());
boolean bIsEmpty = "no name".equals(b.getName());
if (aIsEmpty && !bIsEmpty) {
return 1;
}
if (!aIsEmpty && bIsEmpty) {
return -1;
}
return 0;
}
}
);
This will sort all items with non-empty names ahead of the items with empty names, leaving the relative order of both groups of objects unchanged within their respective group.
If your space constraints allow you to create a new array of MyClass, you can go for a simpler algorithm: go through the original array once, and make a count of non-empty items. Then create a new array, and make two indexes: idxNonEmpty = 0, and idxEmpty = NonEmptyCount+1. Then go through the original array one more time, writing non-empty objects to idxNonEmpty++, and empty objects to idxEmpty++.
ill assume its a method of the class:
public void consolidate()
{
int lengthOfSpaces = spaces.length , i, numberOfEmpties = 0;
Type[] spacesNumberTwo = new Type[lengthOfSpaces ];
// pass 1: find empties
for (i = 0; i < lengthOfSpaces ; i++)
{
if(spaces[i].getName().equals("no name") == false)
spacesNumberTwo[i] = new Type(spaces[i].getName(), spaces[i].getInt());
else
numberOfEmpties++;
}
for (i = lengthOfSpaces - 1; numberOfEmpties > 0 ; numberOfEmpties--, i--)
{
spacesNumberTwo[i] = new Type("no name", 0);
}
spaces = spacesNumberTwo
}

Trouble with asList().contains() comparing variables

I'm making an app where the user has to choose a 4 digit number, and this will be compared to a randomly chosen hidden 4 digit number, but when ever I run the code which should check my array for a comparison between the chosen numbers and the random numbers the 'Arrays.asList().contains())' doesn't seem to pickup on the fact that the array it is checking does have the value it is checking for, any advice?
The code that compares the two variables:-
guess.v1 = code.int1;
guess.v2 = code.int2;
guess.v3 = code.int3;
guess.v4 = code.int4;
int[] guess_list = { guess.v1, guess.v2, guess.v3, guess.v4 };
if (Arrays.asList(guess_list).contains(home.value1)) {
if (code.int1 == home.value1) {
X1.setText("V");
guess.c1 = GuessStatus.V;
} else {
X1.setText("S");
guess.c1 = GuessStatus.S;
}
} else {
X1.setText("X");
guess.c1 = GuessStatus.X;
}
The code that generates the random numbers:-
Code.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent openCode = new Intent(b, code.class);
// adventure_time checks whether there is a saved game already,
// if 1, saved game,
adventure_time = 0;
// random number generation LET THE NUMBER GAMES BEGIN///
Random a1 = new Random();
random1 = new ArrayList<Integer>();
check.fudge = 0;
for (int index = 0; index < 4; index++) {
random1.add(a1.nextInt(5) + 1);
Log.v("MM", "" + random1.get(index));
}
value1 = random1.get(0);
value2 = random1.get(1);
value3 = random1.get(2);
value4 = random1.get(3);
startActivity(openCode);
}
});
You're not calling the Arrays.asList call you think you are. You're actually creating a List<int[]>, not a List<Integer> are you're probably expecting. (There's no such type as List<int> in Java, as it doesn't support generics over primitive types.)
The simplest fix would be to change this:
int[] guess_list = { guess.v1, guess.v2, guess.v3, guess.v4 };
to this:
Integer[] guess_list = { guess.v1, guess.v2, guess.v3, guess.v4 };
You'll then end up creating a List<Integer> which will work appropriately.

Java - Receiving NullPointerException when offering an object to a LinkedList

There is probably a simple solution to this problem I've been having all night. At least I hope there is. When attempting to offer an object to my subQueues LinkedList, I receive a NullPointerException. My program prints out the correct "head" Objects and "digit" integers, but then the Exception is thrown and the program ends.
My program, in short, is supposed to take a mainQueue LinkedList of integers, look at them all one-by-one, and sort them. Its examining the last digit of each integer, and placing them into corresponding subQueues. As of now, I am only to the ones place. After I get past this dilemma, I'll be able to work out the tens, hundreds, etc.
Ex)
mainQueue = { 12 50 215 100 85 539 16 35 }
subQueue[0] = { 50 100 }
subQueue[1] = { }
subQueue[2] = { 12 }
subQueue[3] = { }
subQueue[4] = { }
subQueue[5] = { 215 85 35 }
subQueue[6] = { 16 }
subQueue[7] = { }
subQueue[8] = { }
subQueue[9] = { 539 }
So what am I doing wrong here? Like I said, once I get by this little problem, the rest of the program should be a breeze. Any help is appreciated, thanks!
public class Sorting
{
private LinkedList mainQueue;
private LinkedList[] subQueues;
private final int SIZE = 10;
private int maxDigits; //maximum number of digits
//The constructor instantiates the mainQueue using the LinkedList,
//subQueue array as an array of LinkedList using SIZE(10),
//and initializes maxDigits = 0;
public Sorting()
{
mainQueue = new LinkedList();
for (int i=0; i<SIZE; i++)
{
subQueues = new LinkedList[i];
}
// I have also tried:
// subQueues = new LinkedList[SIZE];
//I get the same runtime error.
maxDigits = 0;
}
public void sortNumbers()
{
while (mainQueue.isEmpty() == false)
{
Object head = mainQueue.peek();
mainQueue.remove();
String digitLine = "" + head;
int digit = Integer.parseInt(digitLine.substring(digitLine.length()-1, digitLine.length()));
System.out.println(head);
System.out.println(digit);
subQueues[digit].offer(head);
}
}
}
You're not correctly building your subQueues it looks like.
If you want an array of SIZE linked-lists, try this:
subQueues = new LinkedList[ SIZE ];
for ( int i = 0; i < SIZE; ++i ) {
subQueues[i] = new LinkedList();
}
Note that this is using raw types as your code is, though preferably you should use parameterized types.

Categories

Resources