I am trying to parse a String into a few variables. The String could contain these 4 tokens: "name, size, age, gender" but they don't all have to be there. Examples of possible Strings.
Example 1. "name:T-rex;"
Example 2. "name:T-rex;size:8;"
Example 3. "name:T-rex;age:4;gender:female"
I tried to do this:
private String name;
private String size;
private String age;
private String gender;
private String parse(String data)
{
String [] parts = data.split(";");
name = parts[0];
size = parts[1];
age = parts[2];
gender = parts[3];
}
But that only works if the String data contains all 4 tokens. How can I solve this problem? I really do need the 4 variables.
The best way is to parse the string into key/value pairs and then call a method that sets them by key:
/**
* Set field based on key/value pair
*/
private void setValue(String key, String value) {
switch(key) {
case "name": {
this.name = value;
break;
}
case "age" : {
this.age = value;
break;
}
//...
}
}
And call it in a programmatic way:
String[] k = "name:T-rex;age:4;gender:female".split(";");
for(String pair: k) {
String[] a = pair.split(":");
setValue(a[0], a[1]);
}
This allows you to be flexible, even to allow some keys to be missing.
Use the magic of hashmaps.
First, split the properties:
String[] parts = inStr.split( ";" );
List<String> properties = Arrays.asList(parts);
Then get name value pairs:
HashMap<String,String> map = new HashMap<String,String>();
Iterator<String> iter = properties.iterator();
for (String property : properties) {
int colPosn = property.indexof(":");
// check for error colPosn==-1
map.put( property.substring(0, colPosn), property.substring(colPosn+1) );
}
Now you can access the properties out of order, and/or test for inclusion like tis:
if(map.containsKey("name") && map.containsKey("age")) {
// do something
String name = map.get("name");
String age = map.get("age");
...
Related
I have a TreeMap with 3 entries, all from individual ArrayLists.
I use the following code:
Map<String, List<String>> mapOne = new TreeMap<String, List<String>>();
List<String> listFour = Arrays.asList("");
ArrayList<String> listOne = new ArrayList<>();
listOne.add("Writer");
listOne.add("Actor");
listOne.add("Politician");
listOne.add("Dancer");
ArrayList<String> listTwo = new ArrayList<>();
listTwo.add("James");
listTwo.add("Robert");
listTwo.add("Tereza");
listTwo.add("John");
ArrayList<String> listThree = new ArrayList<>();
listThree.add("Joyce");
listThree.add("Redford");
listThree.add("May");
listThree.add("Travolta");
for (int i = 0; i < listOne.size(); i++) {
String stringOne = listOne.get(i);
String stringTwo = listTwo.get(i);
String stringThree = listThree.get(i);
listFour = Arrays.asList(stringTwo, stringThree);
mapOne.put(stringOne, listFour);
}
Now I want to obtain the individual String values from the sorted list. like so:
for (Map.Entry<String, List<String>> entry : mapOne.entrySet()) {
String key = entry.getKey();
for (String value : entry.getValue()) {
System.out.println(value);
}
}
The above code prints a list like
{Robert Redford , John Travolta , Tereza May , James Joyce}
Is it possible to iterate over the list in a way as to obtain to separate lists, one with the first names and the other with the last names?
listOne = {Robert , John , Tereza, James}
listTwo = {Redford, Travolta, May, Joyce}
Or should I use an entirely different approach?
The whole thing started out with the need to sort one ArrayList and to other accordingly. What seemed trivial at the beginning, turned out to be a real challenge.
I am a sort of a hobby programmer, so the pros out there kindly bear with me.
It seems you're over-engineering somehow.
I will answer with a Stream solution first, just for the sake of trying it.
Note that I'd, personally, prefer the "old" iterative approach (see below).
// You can see by the use of AtomicInteger that this isn't the right road to take!
final AtomicInteger i = new AtomicInteger();
final Collection<List<String>> values1 =
mapOne.values()
.stream()
.flatMap(v -> v.stream())
.collect(partitioningBy(o -> i.getAndIncrement() % 2 != 0))
.values();
Output: [[Robert, John, Tereza, James], [Redford, Travolta, May, Joyce]]
Iterative approach
final Collection<String> names = new ArrayList<>();
final Collection<String> surnames = new ArrayList<>();
for (final List<String> value : mapOne.values()) {
names.add(value.get(0));
surnames.add(value.get(1));
}
Output:
[Robert, John, Tereza, James]
[Redford, Travolta, May, Joyce]
This is safe because you know each inner List has two elements.
What JB Nizet is telling you to do (credit to him for writing that), is basically to create an appropriate class
public class Person {
private String profession;
private String name;
private String surname;
// Getters and setters. JavaBean style
}
And proceed to sort a Collection<Person>.
For example, keeping it as simple as possible
final List<Person> persons = new ArrayList<>();
// Populate list
Collections.sort(persons, (p1, p2) -> {
// Not null-safe
return p1.getName().compareTo(p2.getName());
});
This will sort the list by name. It will not return a new List, but simply modify the input one.
Putting all the valuable tips, online searches and my efforts together, this is my final solution (in the hope that it might be useful to others). It seems to me my solution is minimal and straightforward :
List<Sites> listOfSites = new ArrayList<Sites>();
for (int i = 0; i < classLists.listSites.size(); i++) {
String site = classLists.listSites.get(i);
String description = classLists.listSitesDesc.get(i);
String link = classLists.listSitesLinks.get(i);
listOfSites.add(new Sites(site, description, link));
}
Collections.sort(listOfSites, (p1, p2) -> {
return p1.getName().compareTo(p2.getName());
});
final ArrayList<String> titles = new ArrayList<>();
final ArrayList<String> descriptions = new ArrayList<>();
final ArrayList<String> links = new ArrayList<>();
for (Sites s : listOfSites) {
String name = s.getName();
String description = s.getDesc();
String link = s.getLink();
titles.add(name);
descriptions.add(description);
links.add(link);
}
Below the class Sites:
public class Sites implements Comparable<Sites> {
private String name;
private String description;
private String link;
public Sites(String name, String description, String link) {
this.name = name;
this.description = description;
this.link = link;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return description;
}
public void setDesc(String description) {
this.description = description;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
}
What I have is an array of String objects called Id, I'm then looping through the Id objects to get the email address associated with that Id (then concatenate them). I now want to remove the String from the array that only has a duplicate email address (ignore by looking at the Id in the String).
For example the String array contains the following Id objects:
{1, 2, 3}
Now I'm concatenating the email address associated with that Id, the String array becomes:
{1 - test#gmail.com, 2 - any#gmail.com, 3 - test#gmail.com}
So I need to remove duplicate emails (along with the Id concatenated to it) to give me this:
{1 - test#gmail.com, 2 - any#gmail.com}
Then after that I will remove the email address by using split to give me the final result of:
{1, 2}
So the issue I'm having is, it off course look for the whole string including email and Id, but I need only to look for email address then remove the whole `String from the array.
I've done this bit of code:
//Remove all duplicate email addresses from list
ArrayList<String> duplicateEmails = new ArrayList<String>();
//looping through cform.getConsumers which is a String[] array of Id's, finding email address of that Id and concatenate it and add to the array list
for (String conId : cform.getToConsumers()){
Long consId = Long.parseLong(conId);
Consumer cons = af.getSingleConsumerId(consId);
duplicateEmails.add(conId + " - " + cons.getEmail());
}
//convert arraylist to String array
String[] stringArray = duplicateEmails.toArray(new String[0]);
//remove the duplicates
Set<String> findDuplicates = new HashSet<String>(Arrays.asList(stringArray));
String[] removedEmails = findDuplicates.toArray(new String[0]);
You can do like following :
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
public class Testing {
public static void main(String[] args) {
HashMap<String, String> hm = new LinkedHashMap<String, String>();
String[] values = {"1","2","3"};
ArrayList<String> ar = new ArrayList<String>();
ar.add("test#gmail.com");
ar.add("any#gmail.com");
ar.add("test#gmail.com");
for (int i = 0; i<ar.size();i++) {
if (!hm.containsKey(ar.get(i)))
hm.put(ar.get(i), values[i]);
}
System.out.println(hm);
}
}
Output :
[1, 2]
Instead of writing Strings to the array, make an array of your own objects. Example:
class MyPair{
String id;
String email;
public String toString(){
return id + " - " + email;
}
}
Then you can compare emails without parsing the whole String again.
Full example with overriding hashCode() and equals(Object):
package main;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
public class Test {
public static void main(String[] args) {
new Test().deduplicate();
}
public class MyPair {
public final String id;
public final String email;
public MyPair(String id, String email) {
super();
this.id = id;
this.email = email;
}
public String toString(){
return id + " - " + email;
}
#Override
public boolean equals(Object obj){
if (!(obj instanceof MyPair))
return false;
if (obj == this)
return true;
return this.email.equals(((MyPair)obj).email);
}
#Override
public int hashCode(){
return this.email.hashCode();
}
}
public void deduplicate() {
List<MyPair> pairs = new ArrayList<MyPair>();
// test data
MyPair p1 = new MyPair("1", "asd#asd.com");
MyPair p2 = new MyPair("2", "qwe#qwe.com");
MyPair p3 = new MyPair("3", "asd#asd.com");
pairs.add(p1);
pairs.add(p2);
pairs.add(p3);
// just to demonstrate the overridden methods
System.out.println(p1.equals(p2));
System.out.println(p1.equals(p3));
System.out.println(p2.equals(p3));
System.out.println(p1.hashCode());
System.out.println(p2.hashCode());
System.out.println(p3.hashCode());
// dedup will be done by HashSet
// This only works because we have overridden
// hashCode and equals!
HashSet<MyPair> deduped = new HashSet<MyPair>();
for (MyPair pair : pairs) {
deduped.add(pair);
}
System.out.println(deduped);
}
}
Output:
[2 - qwe#qwe.com, 1 - asd#asd.com]
(note the changed order! This happens because of the hashing)
Why not using 3 arrays in a way that you keep the indexes for the duplicated eMails in the third array. And then go through the first (ID's) Array and remove those indexes from it.
So you have ID array, eMail Array, Indexes to remove array.
Then release those arrays from memory :)
In that way you dont need to concat strings and then search strings
Just an idea.
I am new to java, and I have this problem; I am working with a webservice from android where I send a request and I get an answer formatted like this string: 1-0,2-0,3-0,4-0,5-0,6-0,7-0,8-0,12-0,13-0 where the number before the "-" means the number of my button and the number after "-" means the button status. I split this string into an array like this:
String buttons = "1-0,2-0,3-0,4-0,5-0,6-0,7-0,8-0,13-0,14-0";
String[] totalButtons = buttons.split(",");
then I make a new request to get the status of my buttons and I get this
String status = "1-0,2-0,3-2,4-0,5-4,6-0,7-4,8-0,9-2,10-1,13-4,14-2";
String[] statusButtons = status.split(",");
The number of the buttons are going to be the same all time; in this case 10 buttons.
The problem that I have is how to compare each element of the two arrays if they can change their status every two seconds and I receive more buttons than the first time and I have to change their status with the new value. For example the first element of the array one is equal to the first element of the second array so there is no problem, but the first array does not have two elements in the second array in this case 9-2,10-1 so they should be deleted. The final result should be like this
String buttons = "1-0,2-0,3-0,4-0,5-0,6-0,7-0,8-0,13-0,14-0";
String status = "1-0,2-0,3-2,4-0,5-4,6-0,7-4,8-0,9-2,10-1,13-4,14-2";
String finalButtons = "1-0,2-0,3-2,4-0,5-4,6-0,7-4,8-0,13-4,14-2";
Here's an idea to get you started;
Map<String,String> buttonStatus = new HashMap<String,String>();
for (String button : totalButtons) {
String parts[] = button.split("-");
buttonStatus.put(parts[0], parts[1]);
}
for (String button : statusButtons) {
String parts[] = button.split("-");
if (buttonStatus.containsKey(parts[0])) {
buttonStatus.put(parts[0], parts[1]);
}
// Java 8 has a "replace" method that will change the value only if the key
// already exists; unfortunately, Android doesn't support it
}
The result will be a map whose keys are taken from the original totalButtons, and whose values will be taken from statusButtons if present. You can go through the keys and values in the Map to get the results, but they won't be in order; if you want them to be in the same order as totalButtons, go through totalButtons again and use buttonStatus.get to get each value.
The javadoc for Map is here.
I would split up each of those again and then compare those values.
ex:
String[] doubleSplit = totalButtons[index].split("-"); // "1-0" -> {"1", "0"}
import java.util.HashMap;
import java.util.Map;
/**
* #author Davide
*/
public class test {
static Map map;
public static void main(String[] args) {
// init value
String buttons = "1-0,2-0,3-0,4-0,5-4,6-0,7-0,8-0,13-0,14-0";
String[] keys = buttons.split("(-[0-9]*,*)");
init(keys);
// new value
String status = "1-0,2-0,3-2,4-0,5-4,6-0,7-4,8-0,9-2,10-1,13-4,14-2";
String[] statusButtons = status.split(",");
update(statusButtons);
print();
}
public static void init(String[] keys) {
map = new HashMap<Integer, Integer>();
for (String k : keys) {
map.put(Integer.valueOf(k), 0);
}
}
public static void update(String[] statusButtons) {
for (String state : statusButtons) {
String[] split = state.split("-");
int k = Integer.valueOf(split[0]);
int v = Integer.valueOf(split[1]);
if (map.containsKey(k)) {
map.put(k, v);
}
}
}
public static void print() {
String out = "";
for (Object k : map.keySet()) {
out += k + "-" + map.get(k) + ",";
}
System.out.println(out.substring(0, out.length() - 1));
}
}
I am decomposing a series of 90,000+ strings into a discrete list of the individual, non-duplicated pairs of words that are included in the strings with the rxcui id values associated with each string. I have developed a method which tries to accomplish this, but it is producing a lot of redundancy. Analysis of the data shows there are about 12,000 unique words in the 90,000+ source strings, after I clean and format the contents of the strings.
How can I change the code below so that it avoids creating the redundant rows in the destination 2D ArrayList (shown below the code)?
public static ArrayList<ArrayList<String>> getAllWords(String[] tempsArray){//int count = tempsArray.length;
int fieldslenlessthan2 = 0;//ArrayList<String> outputarr = new ArrayList<String>();
ArrayList<ArrayList<String>> twoDimArrayList= new ArrayList<ArrayList<String>>();
int idx = 0;
for (String s : tempsArray) {
String[] fields = s.split("\t");//System.out.println(" --- fields.length is: "+fields.length);
if(fields.length>1){
ArrayList<String> row = new ArrayList<String>();
System.out.println("fields[0] is: "+fields[0]);
String cleanedTerms = cleanTerms(fields[1]);
String[] words = cleanedTerms.split(" ");
for(int j=0;j<words.length;j++){
String word=words[j].trim();
word = word.toLowerCase();
if(isValidWord(word)){//outputarr.add(word);
System.out.println("words["+j+"] is: "+word);
row.add(word_id);//WORD_ID NEEDS TO BE CREATED BY SOME METHOD.
row.add(fields[0]);
row.add(word);
twoDimArrayList.add(row);
idx += 1;
}
}
}else{fieldslenlessthan2 += 1;}
}
System.out.println("........... fieldslenlessthan2 is: "+fieldslenlessthan2);
return twoDimArrayList;
}
The output of the above method currently looks like the following, with many rxcui values for some name values, and with many name values for some rxcui:
How do I change the code above so that the output is a list of unique pairs of name/rxcui values, summarizing all relevant data from the current output while removing only the redundancies?
If you just need a Collection of all words, use a HashSet Sets are primarily used for contains logic. If you need to associate a value with your string use a HashMap
public HashSet<String> getUniqueWords(String[] stringArray) {
HashSet<String> uniqueWords = new HashSet<String>();
for (String str : stringArray) {
uniqueWords.add(str);
}
return uniqueWords;
}
This will give you a collection of all the unique Strings in your array. If you need an ID use a HashMap
String[] strList; // your String array
int idCounter = 0;
HashMap<String, Integer> stringIDMap = new HashMap<String, Integer>();
for (String str : strList) {
if (!stringIDMap.contains(str)) {
stringIDMap.put(str, new Integer(idCounter));
idCounter++;
}
}
This will provide you a HashMap with unique String keys and unique Integer values. To get an id for a String you do this:
stringIDMap.get("myString"); // returns the Integer ID associated with the String "myString"
UPDATE
Based on the question update from the OP. I recommend creating an object that holds the String value and the rxcui. You can then place these in a Set or HashMap using a similar implementation to the one provided above.
public MyObject(String str, int rxcui); // The constructor for your new object
MyObject mo1 = new MyObject("hello", 5);
Either
mySet.add(myObject);
will work or
myMap.put(mo1.getStr, mo1.getRxcui);
What is the purpose of the unique word ID? Is the word itself not unique enough since you are not keeping duplicates?
A very basic way would be to keep a counter going as you are checking new words. For each word that doesn't already exist you could increase the counter and use the new value as the unique id.
Lastly, might I suggest you use a HashMap instead. It would allow you to both insert and retrieve words in O(1) time. I am not entirely sure what you are going for, but I think the HashMap might give you more range.
Edit2:
It would be something a little more along these lines. This should help you out.
public static Set<DataPair> getAllWords(String[] tempsArray) {
Set<DataPair> set = new HashSet<>();
for (String row : tempsArray) {
// PARSE YOUR STRING DATA
// the way you were doing it seemed fine but something like this
String[] rowArray = row.split(" ");
String word = row[1];
int id = Integer.parseInt(row[0]);
DataPair pair = new DataPair(word, id);
set.add(pair);
}
return set;
}
class DataPair {
private String word;
private int id;
public DataPair(String word, int id) {
this.word = word;
this.id = id;
}
public boolean equals(Object o) {
if (o instanceof DataPair) {
return ((DataPair) o).word.equals(word) && ((DataPair) o).id == id;
}
return false;
}
}
I have a string in this format(response from EBS Payment Gateway)
key1=value1&key2=value2&key3=value3
How to bind to this class object without using split method?
public class MyClass {
private String key1;
private String key2;
private String key3;
// getter and setter methods
...
}
Try following
public class MyClass {
private String key1;
private String key2;
private String key2;
public MyClass(String k1,String k2,String k3)
{
Key1 = k1;
Key2 = k2;
Key3 = k3;
}
// getter and setter methods
...
}
And while creating object of class
String response = "key1=value1&key2=value2&key3=value3";
String[] keys = response.split("&");
MyClass m = new MyClass(keys[0].split("=")[1],keys[1].split("=")[1],keys[2].split("=")[1])
String template = "key1=value1&key2=value2&key3=value3";
String pattern = "&?([^&]+)=";
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(template);
while (m.find())
{
System.out.println(m.group(1)); //prints capture group number 1
}
Output:
key1
key2
key3
Of course, this can be shortened to:
Matcher m = Pattern.compile("&?([^&]+)=").matcher("key1=value1&key2=value2&key3=value3");
while (m.find())
{
System.out.println(m.group(1)); //prints capture group number 1
}
Breakdown:
"&?([^&]+)=";
&?: says 0 or 1 &
[^&]+ matches 1 or more characters not equal to &
([^&]+) captures the above characters (allows you to extract them)
&?([^&]+)= captures the above characters such that they begin with 0 or 1 & and end with =
NB: Even though we did not exclude = in [^&], this expression works because if it could match anything with an = sign in it, that string would also have an '&' in it, so [^&=] is unnecessary.
Split your string into pieces and then set them using your setters.
String str = "key1=value1&key2=value2&key3=value3";
String[] split = str.split("&");
MyClass obj = new MyClass();
obj.setKey1(split[0].split("=")[1]);
obj.setKey2(split[1].split("=")[1]);
obj.setKey3(split[2].split("=")[1]);
The first split, splits the string at the & symbol.
key1=value1 [0]
key2=value2 [1]
key3=value [2]
After that, you split each of those on the = symbol
key1 [0][0]
value1 [0][1]
key2 [1][0]
value2 [1][1]
key3 [2][0]
value3 [2][1]
So as in the first code block, you have split[0].split("=")[1] which is [0][1] in the explanation below. That's value1
It's quick & dirty but it works perfectly fine :)
Try using beanutils and map
String[] keys = "key1=value1&key2=value2&key3=value3".split("&");
HashMap keyMap = new HashMap();
for(String key:keys){
String[] pair = key.split("=");
keyMap.put(pair[0],pair[1]);
}
MyClass myCls=new MyClass();
BeanUtils.populate(myCls,keyMap);
With Guava you can do this:
String str = "key1=value1&key2=value2&key3=value3";
Map<String, String> map = Splitter.on('&').withKeyValueSeparator("=").split(str);
and than you can do with the keys and values whatever you want. E.g.
mc.setKey1(map.get("key1")); // will set key1 to value1
This can be done by using the split element in java
Store your string in variable and call the split methord in java.
string = "key1=value1&key2=value2&key3=value3";
String[] keys = string.split("&");
IN the next step you can perform a split on each of the elements of the the array keys using the '=' character.
Ref : How to split a string in Java
You can use java reflection :
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MyClass {
private String key1;
private String key2;
private String key3;
public void setKey1(String key1) {
this.key1 = key1;
}
public void setKey2(String key2) {
this.key2 = key2;
}
public void setKey3(String key3) {
this.key3 = key3;
}
public void setKey(String input) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
String[] strings = input.split("&");
String methodName = null;
Method setter = null;
for(String keyValue : strings) {
String[] keyValuePair = keyValue.split("=");
methodName = toSetterMethod(keyValuePair[0]);
setter = getMethod(methodName);
if (setter != null) {
setter.setAccessible(true);
setter.invoke(this, keyValuePair[1]);
}
}
}
private Method getMethod(String methodName) {
try {
Method[] methods = MyClass.class.getMethods();
for (Method method : methods) {
if (method.getName().equals(methodName)) {
return method;
}
}
} catch (SecurityException e) {
}
return null;
}
private String toSetterMethod(String property) {
String setter = "set";
setter += property.substring(0, 1).toUpperCase() + property.substring(1);
return setter;
}
public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
String input = "key1=value1&key2=value2&key3=value3";
MyClass myClass = new MyClass();
myClass.setKey(input);
System.out.println(myClass.key1);
System.out.println(myClass.key2);
System.out.println(myClass.key3);
}
}