How to transform a flat map to complex JSONObject? - java

I want to transform a flat key-value map into a complex json object.
Structure is as follows:
the map-keys of course represent the json keys.
nested elements are separated by a dot
list elements are signaled by a digit at the end of the key. there may be multiple digits at the end. The keys unfortunately stand in reversed order. Mean that the first "key-fragment" maps to the last digit, and the innermost key-fragment maps to the first digit.
Following example:
service.fee.1.1=a
service.fee.2.1=b
service.fee.3.1=c
Here the "service" key maps always to index=1. This means "service" is an array, but with only one element in this case.
The one element has the key "fee", with 3 values inside.
The resulting json should thus be:
{
"service": [
{
"fee": ["a", "b", "c"]
}
]
}
Another example:
service.fee.name.1.1=a
service.fee.age.2.1=b
service.fee.test.2.1=c
{
"service": [
{
"fee": [
{
"name": "a"
},
{
"age": "b",
"test": "c"
}
]
}
]
}
That's what I started with, but I cannot get the point where I probably have to use recursion to handle nested objects and lists:
JSONObject json = new JSONObject();
for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
if (endswithdigit(key)) {
} else {
if (key.contains("-")) {
//complex object
JSONObject subjson = new JSONObject();
json.put(key, subjson);
//TODO probably have to apply some kind of recursion here with subjson??
} else {
//plain value
json.put(key, entry.getValue());
}
}
}
Maybe you could give advise how to properly build a nested JSONObject with nested lists and recursion?

If you need to tackle this problem yourself (IE: a library cannot handle it), then I would break it down so that it can be coherently tackled with Composite Pattern.
I'll address this answer in two parts: first, a proposed solution to create the heirarchy; and second, how to utilize the Composite pattern to turn your internal heirarchy into the JSON you want.
Part 1: Creating the Heirarhcy
One approach for this would be to iteratively create objects by dividing elements in to bins (starting with a common composite root object that contains every element). This will form the composite structure of your data. The flow will be:
For each element in the bin of the object composite:
Strip off the top-level identifier from the left of the element
Create an identifier to be associated with it.
If it is keyed:
Strip off the key from the right
Create a composite array for the identifier (if it does not exist).
If there is further data left of the = of the element:
Create a composite object for the element bin associated with that array index (if it does not exist).
Place the element in a bin for that object.
Otherwise create a leaf node for the value of the index.
Otherwise, if there is further data left of the = of the element:
Create a composite object for the element bin associated with that array index (if it does not exist).
Place the element in a bin for that object.
Otherwise, create a leaf node for the value of the identifier.
Repeat for all new bins.
For example's sake, lets assume we are working with the given dataset:
x.y.z.1.1=A
x.y.z.3.1=B
x.y.w.1.1=C
x.u.1=D
a.b.1=E
a.c.1=F
e.1=G
e.2=H
i=I
m.j=J
m.k=K
The process would then follow as:
ITERATION 0 (initialize):
root // x.y.z.1.1=A, x.y.z.3.1=B, x.y.w.1.1=C, x.u.1=D, a.b.1=E, a.c.1=F, e.1=G, e.2=H, i=I, m.j=J, m.k=K
ITERATION 1:
root :
x[1] // y.z.1=A, y.z.3=B, y.w.1=C, u=D
a[1] // b=E, c=F
e[1] : "G"
e[2] : "H"
i : "I"
m : // j=J, k=K
ITERATION 2:
root :
x[1] :
y[1] // z=A, w=C
y[3] // z=B
u : "D"
a[1] :
b : "E"
c : "F"
e[1] : "G"
e[2] : "H"
i : "I"
m :
j : "J"
k : "K"
ITERATION 3:
root :
x[1] :
y[1] :
z : "A"
w : "C"
y[3] :
z : "B"
u: "D"
a[1] :
b : "E"
c : "F"
e[1] : "G"
e[2] : "H"
i : "I"
m :
j : "J"
k : "K"
Part 2: Composite Pattern
At this point, we've iteratively divided our data into a heirarchical composite structure; now we just need to get our internalized data structure into JSON. This is where the Composite pattern will come in handy; each of your objects will implement the following interface:
// All objects in the composite tree must implement this.
public interface Jsonable {
// The non-leaf objects will need to have their implementation of this
// call it for each child object (and handle gaps).
JsonObject toJsonObject();
}
If following the above, we would likely have three implementations of this interface: ArrayComposite, ObjectComposite, and ValueLeaf.
Calling toJsonObject() on your root element will give you your complete JsonObject. A textural representation of that for the above example is below (notice the added gap in the y array; this needs to be handled in the toJsonObject() call of your array composites):
{
"x" : [
{
"y" : [
{
"z" : "A",
"w" : "C"
},
"",
{
"z" : "B"
}
]
}
],
"a" : [
{
"b" : "D",
"c" : "E"
}
],
"e" : [
"F",
"G"
]
"i" : "I"
"m" : {
"j" : "J",
"k" : "K"
}
}
Which, neglecting white spacing, seems to be what you're looking for.
Note that this assumes a data set does not contain elements that would result in invalid JSON. IE: the dataset could not contain the following:
i=I
i.1=I
As it would be saying that i is both an array and a value.

Please try the Gson library and use new Gson().toJson(yourmap); this will convert your map to JSON format.

Perhaps you resolve it by splitting the key where the numbers start, and using a LIFO for the subkeys and a FIFO for the value and indexes. Instead for splitting it can be done by parsing the key and detecting where the numbers start:
For example:
x.y.z.2.1 = val
This is split to show how it would work, but it can be done just parsing the string (: is to delimit the separation).
x.y.z : 2.1 : val
Then put the subkeys in a LIFO (x goes in first, z last):
LIFO
head: z
y
x
and a FIFO for the value and indexes (2 goes in first, val goes last)
Fifo
top:2
1
val
Then you can pop out from the LIFO and match it to the pop of the FIFO. The first assignment will be for the value of the map, then, the assignments will be done to the object or array of the last step.
z = val
y[1] = z
x[2] = y

Related

How to remove a specific String from a String ArrayList

I have an ArrayList that contains a bunch of words each in their own cell that come from a file. Some of those words are complete word like "physicist, water, gravity". However, other words are just letters that got split throughout the process of the program. For example, "it's" became "it" and "s". As such, I want to remove all of the single letter words except "I" and "A" because these are actual words.
This is the code I have for now:
for(int i=0;i<dictionnary.size();i++) {
if(dictionnary.get(i).compareToIgnoreCase("I")!=0||dictionnary.get(i).compareToIgnoreCase("A")!=0||dictionnary.get(i).length()==1){
dictionnary.remove(i);
}
}
Where dictionnary is my ArrayList. However, when I print out the content of my arrayList the "s" from it's remains. I also know that there was originally a word "E" that got removed throughout the process above. I'm confused as to why the "S" remains and how to fix it.
From my understanding this code goes through the ArrayList and checks if the length of the case is 1 (which is the case for all single letter words) as well as checking if that case is a case of "I" or "A" regardless of if it is capitalized or not. It then removes the cases that don't correspond to the "I" or "A".
Consider using the Collection Iterator for safe removal of elements during iteration.
for (Iterator<String> iter = dictionary.iterator() ; iter.hasNext() ; ) {
String word = iter.next();
if (word.length() == 1
&& !"I".equals(word)
&& !"A".equalsIgnoreCase(word)) {
iter.remove();
}
}
My suggestion is the following:
You can use removeIf in a next way.
removeIf takes a predicate.
public static void main(String[] args) {
List<String> dictionary = new ArrayList<>();
dictionary.add("I");
dictionary.add("A");
dictionary.add("p");
dictionary.add("its");
dictionary.add("water");
dictionary.add("s");
Integer sizeRemove =1;
dictionary.removeIf(
word ->
!"I".equals(word)
&& !"A".equalsIgnoreCase(word)
&& word.length() == sizeRemove
);
System.out.println(dictionary);
}
The output is the following:
[I, A, its, water]
Reference:
https://www.programiz.com/java-programming/library/arraylist/removeif
Use iterators instead. Let's say you have a list of (1,2,3,4,5) and you want to remove the numbers 2 and 3. You start looping through and get to the second element 2. Here your i is 1. You remove that element and go to i=2. What you have now is (1,3,4,5). Since i=2, you have missed one element.
And that's the reason you should use iterators instead. Refer to #vsfDawg answer.

How to use ArrayList to store multiple lines of information?

I created the following code:
int storagevar= arrayList.get(0);
for (int j=0; z<(storagevar-1); j++){
someString= normalArray[j];
}
This code looks through an ArrayList and gets the first element in there, which is a number representing an index in a second (normal) array containing Strings. It then uses this number to get the information from the second array, and stores it in another String.
This code is completely fine; it's the next bit thats causing a problem.
The above code assumes there is only 1 element inside the ArrayList. However, if there is more than one element, then I need to do the operation for each element in the ArrayList, so if there were 3 elements inside it, I would end up with the 3 indexes, and I would use them to extract the information from the normal array, and will end up with 3 Strings.
I wrote the code below for the above scenario. I know it doesn't store each element answer in its own String, and I don't know how to do it; I need help with that too. But anyways, this code doesn't seem to work either:
public void testMethod(){
MyClass test_one = arrayList.get(8);
String[] tmpStringArray = test_one.correct;
ArrayList<Integer> nnaCorrectAnswers = test_one.correctAnswers;
for (int i=0; i<nnaCorrectAnswers.size();i++){
tmp2= nnaCorrectAnswers.get(i);
for (int z=0; z<(tmp2 -1); z++){
someString=tmpStringArray[z];
}
}
}
If I understand correctly, you have an List<Integer> indices containing indices. For example, [2, 4, 6].
And you have an array strings containing Strings. For example: ["a", "b", "c", "d", "e", "f", "g", "h"].
And you want to create a list containing all the elements of the array whose indices are stored in the list. So, for example: ["c", "e", "g"] (because "c" is at index 2, "e" is at index 4, and "g" is at index 6).
Is that right? If so, all you need is:
List<String> result = new ArrayList<>();
for (int index : indices) {
result.add(strings[index]);
}
Instead of always getting index 0, do a for-each loop:
for(int number : arrayList){
someString = normalArray[number-1];
//...
}

Given a list of subsrings and a string, print out the item in the list if it is a substring

Given a list of subsrings and a string, print out the item in the list if it is a substring.
What's the most efficient way to do this without using any substring methods?
Example:
Input: ["cat", "dog", "foo", "foopoo", "foopo", "nope", "dogf"]
Output: "catdogfoopoo"
Explanation:
"c" is in the map at index 0.
Loop through the length of "cat" and compare if its the same as from 0 to the length of "cat"
If yes, print.
My idea:
So my idea so far is that you would loop through the string given and map each character to an arraylist of indices
<(c, [0]), (a, [1]), (r, [2]), (d, [3]), (o, [4,7,8,10,11]), (g, [5]), (f, [6]), (p, [4])>
and then you loop through the list of substrings.
for (int x = 0; x < list.length; x++) {
String s = list.get(x);
if (s.get(s.charAt(0)) != null)
//loop through, comparing from whether the word is in the string
but this doesn't take advantage of the fact that if "foo" was already found to be a substring, it should be easy to check if "foopoo" is a substring (without looping through foo already)
I'm a bit stuck there because I'm sure there's a more efficient way to do this. Without using "contains" or whatnot (which is NOT more efficient)
for(int i=0; i < array.length; i++){
if(input.contains(array[i])){
System.out.println(array[i]);
}
}
This should be a solution that covers this issue.
String text = "cardogfoopoo";
for (int x = 0; x < list.length; x++) {
if(text.containts(list[x])
System.out.println(list[x] + " is a substring!");
}
Maybe you should sort the substrings and generate a data structure similar to this (it is an array of linked lists):
[0] cat
[1] dog -> f
[2] foo -> po -> o
[3] nope
In this structure you can find the substrings "dog" and "dogf", as well as "foo", "foopo" and "foopoo". It is similar to searching in a dictionary.
You start at index 0 of your "catdogfoopoo" string, and starts to search through the dictionary, i.e. look for an entry which starts with 'c'. You find out that index 0 of the structure contains 'c', so you decide to walk through its length. So you take the entry length (3 characters) and see if the three characters starting at index 0 are equal to it (it's a string comparison, it can't be helped). They are, so you print "cat" because you found a whole entry that is contained in your string.
Next index is 3 i.e. character 'd', and you are at the first entry of position [0] of the dictionary. There are no further entries in this position so you must look for a different entry starting with 'd' so you find "dog" as the first entry of position [1]. You compare this entry (which is made out of three characters) with the characters in the string starting at index 4 and you find they are equal, so you print "dog". Next index is 6 and you compare it the next entry which is "f" and you find they are equal so you print "dogf".
And so on until you print "foo", "foopo" and "foopoo".
Hope it is clear. If you find it useful I can provide the details on how I created the structure.

Recursive Linked Lists in Java

I am working on an assignment that has me writing a java program to print out in reverse order the data contained in a linked list using recursion. So far this is what I have, it works but only on the final element in the list IE. it stops once it prints the last element.
public String reverse(IntNode head){
String result = "";
if(head.getLink() == null){
result += head.getData();
return result;
}
else if(head.getLink().getLink() == null){
result += head.getLink().getData();
head.removeNodeAfter();
return result;
}
else{
return reverse(head.getLink());
}
}
How would I get it to keep going through the list backwards up the recursive tree?
As others have pointed out, your solution is more complicated than it needs to be.
First, note that you don't need to (and probably don't want to) remove any items from the list in order to traverse it.
Second, rather than checking if the current node's link is null, you can actually check if the current link itself is null (nothing wrong with that so long as you don't attempt to dereference it). This simplifies the logic.
public String reverse(IntNode head) {
if (head == null)
return "";
else
return reverse(head.getLink()) + head.getData();
}
Or you could even write it like this:
public String reverse(IntNode head) {
return (head == null)? "" : reverse(head.getLink()) + head.getData();
}
When thinking about recursion, it's a good strategy to build up from the ground, until you hit interesting cases. At each step, try to reuse previous cases.
Q1. What is the reverse of an empty list? []
A1. An empty list. []
Q2. What is the reverse of a list with a single element? [ 1 ]
A2. The same list. [ 1 ]
Q3. What is the reverse of a list with two elements? [ 1 2 ]
A3. The first element, appended to the second element. [ 2 1 ] (Starting to get interesting)
Q4. What is the reverse of a list with three elements? [ 1 2 3 ]
A4. The first element, appended to the reverse of the list of the remaining elements. [ 3 2 ] + [ 1 ] = [ 3 2 1]
Now the pattern should start to be clear: the reverse of a list of N elements is the reverse of the last N - 1 elements with the first element appended).
What is our base case? From the above, it appears having a list of 1 or 0 elements. Look closer though, applying our pattern to a list of length 1, we see that [ 1 ] reversed is [] with the first element appended, ie [] + [ 1 ]. So really, our base case is just the empty list.
This code is more complicated than it needs to be. In this case the recursion can be broken down into 2 cases (or 2 execution paths the function might have):
Recursive case: this path calls itself so that recursion can start (or continue)
Base case: this path ends the recursion by returning without calling itself
Your code has 2 base cases, and it only needs 1. Think about all the different inputs you can call the function with:
head is null
head is an IntNode:
a. head.getLink() returns null
b. head.getLink() returns an IntNode:
head.getLink().getLink() returns null
head.getLink().getLink() returns an IntNode
You'll start to notice a pattern; once it's simplified there are really only 2 possible inputs:
head is null
head is an IntNode
If head is an IntNode, the function can call itself with head.getLink() (the recursive case) and it can be only those same 2 inputs, and this continues until head becomes null (the base case).
To answer your actual question, it only returns the last element's value because in the recursive case, you're not actually prepending the value of the current IntNode (i.e. head.getData()); you're just returning the result of the next reverse() call, which means it will recurse until it gets to the last element, and return just that element's value.

Algorithm to find the simplest combination of integers that has not yet been used

I am looking for an algorithm for finding the simplest combination of integers from 0 to 5 (that is the one that consists of the fewest number of integers) that has not yet been used (the used combinations are in a list).
The order does matter and the combinations should be returned in a list.
For example, the list with the used numbers could look like this:
{{0},{1},{2},{3},{4},{0,0},{0,1},{0,2},...,{2,1},{2,2},...,{1,5,4},...}
In this case, the algorithm should return a list with {5}, as {5} is the combination that consists of the fewest integers.
If the list looks like this:
{{0},{1},{2},{3},{4},{5},{0,0},{0,1},{0,2},{0,3},{0,5},...}
the algorithm should return a list with 0 and 4 ({0,4}).
As it is to be used in Java, a Java answer is preferable but pseudo-code or other programming languages are usable too.
Thank you in advance!
I guess example 2 is wrong:
for {{0},{1},{2},{3},{4},{5},{0,1},{0,2},{0,3},{0,5},...} smallest solution is {0,0}, not {0,4}
Complete solutions is here:
import java.util.*;
public class Algorithm {
static List<List<Integer>> getChildren(List<Integer> node){
List<List<Integer>> children = new ArrayList<List<Integer>>();
for(int i = 0; i < 6; i++){
List<Integer> child = new ArrayList<Integer>(node);
child.add(i);
children.add(child);
}
return children;
}
static List<Integer> find(Queue<List<Integer>> queue, Set<List<Integer>> set){
for(;;){
List<Integer> head = queue.poll();
if(!set.contains(head)){
return head;
} else {
for(List<Integer> child : getChildren(head)){
queue.add(child);
}
}
}
}
public static void main(String[] arg) {
Queue<List<Integer>> queue = new LinkedList<List<Integer>>();
for(int i = 0; i < 6; i++){
queue.add(Collections.singletonList(i));
}
// Example {{0},{1},{2},{3},{4},{5},{0,1},{0,2},{0,3},{0,5},...}
Set<List<Integer>> task = new HashSet<List<Integer>>();
task.add(Arrays.asList(0));
task.add(Arrays.asList(1));
task.add(Arrays.asList(2));
task.add(Arrays.asList(3));
task.add(Arrays.asList(4));
task.add(Arrays.asList(5));
task.add(Arrays.asList(0, 1));
task.add(Arrays.asList(0, 2));
task.add(Arrays.asList(0, 3));
task.add(Arrays.asList(0, 5));
System.out.println(find(queue, task));
}
}
If the list you have is ordered, there are 2 methods I can think of that would be better than a linear search.
Assuming that you will not completely fill the combination space, you can use a variation of a binary search.
First, lets call each set of size 'x' a group. So, 0,1,2,3,4,5 is group 1, {0,0} to {5,5} is group 2.
Starting with group 1, check the list position that contain the last value in the group if they were all there. Eg, List[5] == 5. If it does, move on to group 2 and repeat. If it doesn't, proceed to do a binary search within just that group always favoring the lower side, eventually you will find the first missing value.
Otherwise if you expect to use the entire combination space eventually, just do a binary search on the entire combination space, checking if the value at the position matches the expected value if the preceding values all existed.
A complete (naive) solution:
import java.util.*;
public class Test {
public static String increment(String str) {
if (str.isEmpty()) return "0";
int i = str.length() - 1;
if (str.charAt(i) < '5')
return str.substring(0, i) + (char) (str.charAt(i) + 1);
return increment(str.substring(0, i)) + "0";
}
public static String nextUnused(Set<String> used) {
String s = "0";
while (used.contains(s))
s = increment(s);
return s;
}
public static void main(String args[]) {
Set<String> used = new HashSet<String>(Arrays.asList("0", "1", "2", "3",
"4", "00", "01", "02", "21", "22", "154"));
for (int i = 0; i < 10; i++) {
String toUse = nextUnused(used);
System.out.println("Adding " + toUse);
used.add(toUse);
}
}
}
Output:
Adding 5
Adding 03
Adding 04
Adding 05
Adding 10
Adding 11
Adding 12
Adding 13
Adding 14
Adding 15
You could probably speed it up quite a bit by applying memoization to the increment-method.
Just try each combination in order, starting with the shortest, and stop when you have one which isn't used? Did you try that, it seems very obvious indeed?
For that problem, I would create a specific object to store an element (single number or tuple of numer) :
class Tuple {
String key;
Set<Integer> tuple;
}
The key would be the contatenation of the numbers, ordered.
In your example, the keys would be "0" "1" "2" "3" "4" "5" "01" "02" "03" "05".
You can use a Map to store the tuples, with the association key - value.
Since the keys respect a logical order, finding the next free tuple is easy. You just start from "0" and increment the key (using the defined order), checking in the Map to verify if the tuple is already used or not.
In this example, the first free tuple has the key "04". From this key, creating the associated Tuple is easy.

Categories