how to manipulate list in java - java

Edit: My list is sorted as it is coming from a DB
I have an ArrayList that has objects of class People. People has two properties: ssn and terminationReason. So my list looks like this
ArrayList:
ssn TerminatinoReason
123456789 Reason1
123456789 Reason2
123456789 Reason3
568956899 Reason2
000000001 Reason3
000000001 Reason2
I want to change this list up so that there are no duplicates and termination reasons are seperated by commas.
so above list would become
New ArrayList:
ssn TerminatinoReason
123456789 Reason1, Reason2, Reason3
568956899 Reason2
000000001 Reason3, Reason2
I have something going where I am looping through the original list and matching ssn's but it does not seem to work.
Can someone help?
Code I was using was:
String ssn = "";
Iterator it = results.iterator();
ArrayList newList = new ArrayList();
People ob;
while (it.hasNext())
{
ob = (People) it.next();
if (ssn.equalsIgnoreCase(""))
{
newList.add(ob);
ssn = ob.getSSN();
}
else if (ssn.equalsIgnoreCase(ob.getSSN()))
{
//should I get last object from new list and append this termination reason?
ob.getTerminationReason()
}
}

To me, this seems like a good case to use a Multimap, which would allow storing multiple values for a single key.
The Google Collections has a Multimap implementation.
This may mean that the Person object's ssn and terminationReason fields may have to be taken out to be a key and value, respectively. (And those fields will be assumed to be String.)
Basically, it can be used as follows:
Multimap<String, String> m = HashMultimap.create();
// In reality, the following would probably be iterating over the
// Person objects returned from the database, and calling the
// getSSN and getTerminationReasons methods.
m.put("0000001", "Reason1");
m.put("0000001", "Reason2");
m.put("0000001", "Reason3");
m.put("0000002", "Reason1");
m.put("0000002", "Reason2");
m.put("0000002", "Reason3");
for (String ssn : m.keySet())
{
// For each SSN, the termination reasons can be retrieved.
Collection<String> termReasonsList = m.get(ssn);
// Do something with the list of reasons.
}
If necessary, a comma-separated list of a Collection can be produced:
StringBuilder sb = new StringBuilder();
for (String reason : termReasonsList)
{
sb.append(reason);
sb.append(", ");
}
sb.delete(sb.length() - 2, sb.length());
String commaSepList = sb.toString();
This could once again be set to the terminationReason field.
An alternative, as Jonik mentioned in the comments, is to use the StringUtils.join method from Apache Commons Lang could be used to create a comma-separated list.
It should also be noted that the Multimap doesn't specify whether an implementation should or should not allow duplicate key/value pairs, so one should look at which type of Multimap to use.
In this example, the HashMultimap is a good choice, as it does not allow duplicate key/value pairs. This would automatically eliminate any duplicate reasons given for one specific person.

What you might need is a Hash. HashMap maybe usable.
Override equals() and hashCode() inside your People Class.
Make hashCode return the people (person) SSN. This way you will have all People objects with the same SSN in the same "bucket".
Keep in mind that the Map interface implementation classes use key/value pairs for holding your objects so you will have something like myHashMap.add("ssn",peopleobject);

List<People> newlst = new ArrayList<People>();
People last = null;
for (People p : listFromDB) {
if (last == null || !last.ssn.equals(p.ssn)) {
last = new People();
last.ssn = p.ssn;
last.terminationReason = "";
newlst.add(last);
}
if (last.terminationReason.length() > 0) {
last.terminationReason += ", ";
}
last.terminationReason += p.terminationReason;
}
And you get the aggregated list in newlst.
Update: If you are using MySQL, you can use the GROUP_CONCAT function to extract data in your required format. I don't know whether other DB engines have similar function or not.
Update 2: Removed the unnecessary sorting.

Two possible problems:
This won't work if your list isn't sorted
You aren't doing anything with ob.getTerminationReason(). I think you mean to add it to the previous object.

EDIT: Now that i see you´ve edited your question.
As your list is sorted, (by ssn I presume)
Integer currentSSN = null;
List<People> peoplelist = getSortedList();//gets sorted list from DB.
/*Uses foreach construct instead of iterators*/
for (People person:peopleList){
if (currentSSN != null && people.getSSN().equals(currentSSN)){
//same person
system.out.print(person.getReason()+" ");//writes termination reason
}
else{//person has changed. New row.
currentSSN = person.getSSN();
system.out.println(" ");//new row.
system.out.print(person.getSSN()+ " ");//writes row header.
}
}
If you don´t want to display the contents of your list, you could use it to create a MAP and then use it as shown below.
If your list is not sorted
Maybe you should try a different approach, using a Map. Here, ssn would be the key of the map, and values could be a list of People
Map<Integer,List<People>> mymap = getMap();//loads a Map from input data.
for(Integer ssn:mymap.keyset()){
dorow(ssn,mymap.get(ssn));
}
public void dorow(Integer ssn, List<People> reasons){
system.out.print(ssn+" ");
for (People people:reasons){
system.out.print(people.getTerminationReason()+" ");
}
system.out.println("-----");//row separator.
Last but not least, you should override your hashCode() and equals() method on People class.
for example
public void int hashcode(){
return 3*this.reason.hascode();
}

Related

Checking if an attribute is equal to a list of string values

According to a business requirement, I will need not check if the value of an attribute if it equal to a list of given string values.
I am wondering what is the better way to do it, in case if someday there is a new value that needs to be added. Should these values be stored in a table?
List<String> values = new ArrayList<>();
values.add("value1");
values.add("value2");
values.add("value3");
if(values.contains(brand){
// if the brand contains the given values
// implement a specific logic
}
Thank you
You could do it like this. If there was a Brand class that returned an immutable list of attributes.
Brand brand = new Brand(...);
List<String> values = new ArrayList<>();
values.add("value1");
values.add("value2");
values.add("value3");
if(brand.getAtrributes().containsAll(values)) {
// do something.
}
But imo, it would be better to use an EnumSet and define the attributes as enums.
enum Attr {VALUE1, VALUE2, VALUE3,VALUE4, VALUE5};
EnumSet<Attr> attr = EnumSet.of(Attr.VALUE1, Attr.VALUE2, Attr.VALUE3, Attr.VALUE4, Attr.VALUE5);
if(attr.contains(Attr.VALUE1)) {
// do something.
}
There is still a containsAll method as well as other potentially helpful methods.
create a record that takes an set of enums
create an instance of that with values 2-4.
And use the range feature to verify.
record Brand(EnumSet<Attr> getAttributes){}
Brand brand = new Brand(EnumSet.of(Attr.VALUE2,Attr.VALUE3, Attr.VALUE4));
EnumSet<Attr> required = EnumSet.range(Attr.VALUE2,Attr.VALUE4);
if (brand.getAttributes().containsAll(required)) {
System.out.println("Good to go");
}
Prints
Good to go.

LinkedHashMap with values as a vector being overwritten

When I wrote this piece of code due to the pnValue.clear(); the output I was getting was null values for the keys. So I read somewhere that adding values of one map to the other is a mere reference to the original map and one has to use the clone() method to ensure the two maps are separate. Now the issue I am facing after cloning my map is that if I have multiple values for a particular key then they are being over written. E.g. The output I am expecting from processing a goldSentence is:
{PERSON = [James Fisher],ORGANIZATION=[American League, Chicago Bulls]}
but what I get is:
{PERSON = [James Fisher],ORGANIZATION=[Chicago Bulls]}
I wonder where I am going wrong considering I am declaring my values as a Vector<String>
for(WSDSentence goldSentence : goldSentences)
{
for (WSDElement word : goldSentence.getWsdElements()){
if (word.getPN()!=null){
if (word.getPN().equals("group")){
String newPNTag = word.getPN().replace("group", "organization");
pnValue.add(word.getToken().replaceAll("_", " "));
newPNValue = (Vector<String>) pnValue.clone();
annotationMap.put(newPNTag.toUpperCase(),newPNValue);
}
else{
pnValue.add(word.getToken().replaceAll("_", " "));
newPNValue = (Vector<String>) pnValue.clone();
annotationMap.put(word.getPN().toUpperCase(),newPNValue);
}
}
sentenceAnnotationMap = (LinkedHashMap<String, Vector<String>>) annotationMap.clone();
pnValue.clear();
}
EDITED CODE
Replaced Vector with List and removed cloning. However this still doesn't solve my problem. This takes me back to square one where my output is : {PERSON=[], ORGANIZATION=[]}
for(WSDSentence goldSentence : goldSentences)
{
for (WSDElement word : goldSentence.getWsdElements()){
if (word.getPN()!=null){
if (word.getPN().equals("group")){
String newPNTag = word.getPN().replace("group", "organization");
pnValue.add(word.getToken().replaceAll("_", " "));
newPNValue = (List<String>) pnValue;
annotationMap.put(newPNTag.toUpperCase(),newPNValue);
}
else{
pnValue.add(word.getToken().replaceAll("_", " "));
newPNValue = pnValue;
annotationMap.put(word.getPN().toUpperCase(),newPNValue);
}
}
sentenceAnnotationMap = annotationMap;
}
pnValue.clear();
You're trying a bunch of stuff without really thinking through the logic behind it. There's no need to clear or clone anything, you just need to manage separate lists for separate keys. Here's the basic process for each new value:
If the map contains our key, get the list and add our value
Otherwise, create a new list, add our value, and add the list to the map
You've left out most of your variable declarations, so I won't try to show you the exact solution, but here's the general formula:
List<String> list = map.get(key); // try to get the list
if (list == null) { // list doesn't exist?
list = new ArrayList<>(); // create an empty list
map.put(key, list); // insert it into the map
}
list.add(value); // update the list

Check value inside Map

I have a Map where I save values with the form NAME-GROUP.
Before doing some operations, I need to know if the Map contains a specific group,
for example: I need to check for values containing group1 like Mark-group1.
I'm trying to get it this way:
if (checkList.containsValue(group1)) {
exists = true;
}
I can't provide the name when searching because there could be diferent names with the same group.
But it isn't finding the value, as seems that this function just looks for the entire value string and not only for part of it.
So, there would be any way of achieving this, or would I need to change the way I'm focusing my code.
Update--
This is the looking of my Map:
Map<Integer, String> checkList = new HashMap<Integer, String>();
I load some values from a database and I set them into the Map:
if (c.moveToFirst()) {
int checkKey = 0;
do {
checkKey++;
checkList.put(checkKey, c.getString(c.getColumnIndex(TravelOrder.RELATION)));
}while(c.moveToNext());
}
The relation column, has values like: mark-group1, jerry-group1, lewis-group2, etc...
So, the Map will have a structure like [1, mark-group1], etc...
What I need is to check if there is any value inside the map that contains the string group1 for example, I don't care about the name, I just need to know if that group exists there.
If you want to check any value contain your string as a substring you have to do the following:
for (String value : yourMap.values()) {
if (value.contains(subString)) {
return true;
}
}
return false;
By the way if your values in the map are really have two different parts, i suggest to store them in a structure with two fields, so they can be easily searched.

Change value of multidimensional ArrayList

I need a solution to change the value of an element in a multidimensional ArrayList in Java. This is my ArrayList:
public class Users {
ArrayList<ValidateUser> personer = new ArrayList<ValidateUser>();
public Users() {
personer.add(new ValidateUser("admin", "asdf123", 0.8, "admin"));
personer.add(new ValidateUser("jesper", "ukamm19", 2.5, "user"));
personer.add(new ValidateUser("lars", "lol123", 1.5, "user"));
}
I want to change the double value (0.8) at the user "admin", for example.
This would be done from another a class.
How to do so?
Thanks in advance! :)
As I've stated in my comment, just iterate through the list to find the object. If you're going to do this a lot, consider using a map.
for (ValidateUser user : personer)
if (user.getName().equals("admin"))
user.setNumber(someNumber);
First, note that this is not a multidimensional array, is just a list that holds elements of ValidateUser class object references. Second, you need to access to the element before updating it. You have several ways to accomplish this:
Implement the equals and hashCode methods in your ValidateUser class, then just retrieve the object from your List:
ValidateUser adminUser = personer.get(new ValidateUser("admin", "", 0.8, ""));
adminUser.set...
Note: this looks ridiculous but will work (assuming your equals method only checks by the field that holds this "admin" value.
Navigate through the array and seek for the desired element manually, then update it:
for (ValidateUser user : personer) {
if ("admin".equals(user.getXxx()) {
user.set...
break; //don't forget this!
}
}
Use a different data structure like a Map<String, ValidateUser> to store your data and faster retrieval:
Map<String, ValidateUser> personerMap = new LinkedHashMap<String, ValidateUser>();
personerMap.add("admin", new ValidateUser("admin", ...);
//fill the map with other values...
//if you still want a Collection<ValidateUser> personer variable
Collection<ValidateUser> personer = personerMap.values();
//now check for the desired element
ValidateUser admin = personerMap.get("admin");
if (admin != null) {
admin.set...
}
By comments, your ValidateUser is an immutable object, so you cannot update its fields using setters (because there aren't). So, the best approach here is to use a ListIterator<ValidateUser> instead (not to confuse with Iterator) and replace the element by your modified object. Here's an example:
//the new immutable ValidateUser that will replace the older one...
//set the parameters as needed
ValidateUser newAdmin = new ValidateUser("admin", ...);
ListIterator<ValidateUser> listIterator = personer.listIterator();
while (listIterator.hasNext()) {
ValidateUser validateUser = listIterator.next();
if ("admin".equals(validateUser.getXxx()) {
listIterator.set(newAdmin);
break;
}
}

Getting duplicate values while comparing with the map

Initially i will be selecting a list of student names in a page and submit for getting the address details.StudentNames will be stored in the studentDetailMapList.While looping through the list , i will compare the student names with the AddressDetailsMap to retrieve the addressDetails.But when there are students with same names , the first iteration returns the exact address but when the second iteration happens , it again returns the 1st student address instead of the second student address.It is getting the duplicate values
for (i=studentDetailMapList.values().iterator;i.hasNext();)
{
detailMap = (Map)i.Next();
sDetails = (StudentDetails)detailMap.get("Student");
student = sDetails.getRollNo();
StudentAddressDetails studentAddressDetails = getDetailswithAddress(AddressDetailsMap,sDetails);
}
private StudentAddressDetails getDetailswithAddress(Map AddressDetailsMap,sDetails student)
{
StudentAddressDetails addDetails = null;
try{
for(Iterator itr = AddressDetailsMap.values().iterator();itr.hasNext();){
addDetails = (StudentAddressDetails )itr.next();
if( (addDetails != null) && (addDetails.getStudentID().equals(student.getId()))){
return addDetails;
}
}
}catch(Throwable t){
return null;
}
return null;
}
Is there a way to avoid the duplicate while comparing with the map?
Thanks a lot.
The problem you are having is that you are using the the map data structure wrong.
A map is an object that maps keys to values. A map cannot contain duplicate keys; each key can map to at most one value.
You can not have multiple addresses for the same name, you should use other property for the map, maybe a Student ID, even the list's index should work in this case.
You have to add a UUID in your student's class and work with it for managing their data
EDIT for response
Java's UUID give you the basic Java's UUID.
you can manage your own sequence - This way may be helpfull for indexing Students in database with UNIQUE_ID.
The best thing to manage your IDs : use a HashMap<Integer,Student>.
Each key of your AddressDetailsMap map must be an identifier (studentId) and not the student name.
EDIT:
In this case, your method should look like this:
private StudentAddressDetails getDetailswithAddress(Map AddressDetailsMap,sDetails student) {
return AddressDetailsMap.get(student.getStudentID());
}

Categories

Resources