List and HashMap group - java

I just had an issue that I couldn't solve and I would like your opinion.
I have reached at a stage after reading some input files where I have an Array List of objects which includes a number and a color and from the other side I have a HashMap which includes these numbers with names associated.What I want to do is compare the numbers in these two collections and in the end group them by name.That's what I have at the moment and the output I want to succeed.
INPUT
HashMap names
1-Bill
2–John
3-Jason
4-Jack
5-Michael
6-Chris
ArrayList numbers
2-red
3-yellow
1-green
2-pink
2-gold
1-pink
4-brown
DESIRED OUTPUT
Bill [ green , pink ]
John[ red , pink , gold ]
Jason[ blue , red ]
Jack [ brown ]
I have written this code:
public Map<String,String> getAllDetails(){
HashMap<String , String> theEnd =new HashMap<String ,String>();
for (Numbers t : numbers ) {
String plate = t.getNumber();
for (Map.Entry<String, String> entry : names.entrySet()) {
String key = entry.getKey();
if(plate.equals(key)) {
theEnd.put( t.getName() ,entry.getValue());
}
}
}
return theEnd;
}
And the result I get is Pink = Bill , gold = John , red = Jason ,brown=Jack
So for each color I get a name instead of getting all the colors for each name.
What can I do to get all the colors and group them by name??
Thank you very much in advance.

I think you should use a Map<String,String[]> or Map<String,ArrayList<String>> and for each name (String) you create a list (ArrayList<String> or String[]) and you add the colors to the created list.

Your program has logically problem. Do change like
Convert map value as List like HashMap<String , List<String>> theEnd =new HashMap<String ,List<String>>
First loop should be on names, than on numbers. You need to check for each person has key i.e.1 significant how may color. (numbers).
Add inner number object values into List<String>
After complete looping, do looping on theEnd map and desplay values.
Finally theEnd has value each member respectively colors.

You should try to find names for your classes/variables/methods that means something for the domain at hand. For instance theEnd is not really explicit. Technically, the method getAllDetails does a join of 2 collections on the "plate number" key.
Anyway, here is a commented solution to your problem :
As I understand, you have some class Number that contains a "plate" number and a color. Depending on what you intend to do with this bean class, you should complete with equals and hashcode methods.
public class Number {
String plate;
String color;
public Number(String plate, String color) {
this.plate = plate;
this.color = color;
}
public String getPlate() {
return plate;
}
public String getColor() {
return color;
}
}
I suppose numbers and names are collections declared in global scope for getAllDetails.
List<Number> numbers = new ArrayList<>();
Map<String, String> names = new HashMap<>();
The result of getAllDEtails is a Map whose key is a String that holds the name, and the value is a Set of String to hold the colors.
public static Map<String, Set<String>> getAllDetails()
{
// No duplicate of color for each name => use a Set
Map<String , Set<String>> theEnd = new HashMap<>();
for (Number t : numbers) {
String plate = t.getPlate();
// find name associated with plate
String name = names.get(plate);
if (name != null) {
// find if the corresponding name exists in result map
Set<String> colors = theEnd.get(name);
// If the name is not already there create the Set and put the entry
if (colors == null) {
colors = new HashSet<>();
theEnd.put(name, colors);
}
// Add the color to the Set
colors.add(t.getColor());
}
}
return theEnd;
}
And you can test this with :
public static void main(String [] args) {
numbers.add(new Number("2", "red"));
numbers.add(new Number("3", "blue"));
numbers.add(new Number("2", "gold"));
numbers.add(new Number("1000", "black"));
names.put("2", "Joe");
names.put("3", "Mickael");
System.out.println(getAllDetails());
}
It gives : {Joe=[red, gold], Mickael=[blue]}

I'm changing your map and list names just to make it easier to read, although your Numbers class is little clueless because you call the property as name but you actually mean a color name, I'm not changing that now but I suggest you change that as well :)
If you're on JDK7 or above, you can use diamond operator skipping type definition on right hand side, something like this
Map<String,String> namesMap = new HashMap<>();
List<Numbers> numbers = new ArrayList<>();
coming to your getAllDetails(), the problem with your code is that your Map values are of type String but you actually want to capture a list of color strings, so change your theEnd map like below.
Map<String , List<String>> theEnd =new HashMap<>();
/* As you want a map( name, list(colors) ), start iterating on map
so that it's easy to read your logic. Also iterate on keySet
of numbers rather than on entries */
for(String num : namesMap.keySet()){
// get the person name for a number
String name = namesMap.get(num);
for(Numbers number : numbers){
// get the number from each Numbers object
String plate = number.getNumber();
if(plate.equals(num)){
// if numbers match get the colorsList by name
List<String> colorList = theEnd.get(name);
/*first time you find a number, colorsList
will be null, so initialize it to empty list*/
if( colorList == null) {
colorList = new ArrayList<String>();
}
colorList.add(number.getName());
/* after adding your color to colorsList,
update the entry in map again */
theEnd.put(name, colorList);
}
}
}
// Finally print your values
for(String person : theEnd.keySet()){
System.out.println(person+"->"+theEnd.get(person));
}
With this, for your input, I got the following output
Bill->[green, pink]
John->[red, pink, gold]
Jason->[yellow]
Jack->[brown]

Related

Given a Map with values of type string, which are all numbers separated by commas, would it be possible to transform each value into a double?

I recently asked about converting Json using Gson into something I can sort values into, and the best option was using a Linked HashMap.
List<String> stringList = Arrays.asList(tm.split(" |,")); // split into pair key : value
Map<String, List<String>> mapString = new LinkedHashMap<>();
stringList.forEach(s1 -> {
String[] splitedStrings = s1.split(": "); //split into key : value
String key = splitedStrings[0].replaceAll("[^A-Za-z0-9]",""); // remove non alphanumeric from key, like {
String value = splitedStrings[1];
if (mapString.get(key) == null) {
List<String> values = new ArrayList<>();
values.add(value);
mapString.put(key, values);
}else if (mapString.get(key) != null) {
mapString.get(key).add(value);
}
});
When this code is run, a map with keys for frequency, magnitude, and other attributes of my data is created. This is the original Json Message compared to the resulting map value for the same set of data (Formatted to make it easier to understand and look better)
{"groupId":"id3_x_","timestamp":1.591712740507E9,"tones":
[{"frequency":1.074,"level":3.455,"bw":0.34,"snr":3.94,"median":0.877},
{"frequency":14.453,"level":2.656,"bw":0.391,"snr":2.324,"median":1.143},
{"frequency":24.902,"level":0.269,"bw":0.282,"snr":2.216,"median":0.121},
{"frequency":22.607,"level":0.375,"bw":0.424,"snr":2.034,"median":0.184},
{"frequency":9.863,"level":2.642,"bw":0.423,"snr":1.92,"median":1.376}]}
To Map values:
Message Received
Group ID: id3_x_
Message Topic: pi7/digest/tonals
Time of Arrival: 1.591712740507E9
---------------DATA---------------
Frequency: [1.07, 14.45, 24.90, 22.61, 9.86]
Magnitude: [3.46, 2.66, 0.27, 0.38, 2.64]
Bandwidth: [0.34, 0.39, 0.28, 0.42, 0.42]
SNR: [3.94, 2.32, 2.22, 2.03, 1.92]
Median: [0.88, 1.14, 0.12, 0.18, 1.38]]
While this is very useful for analyzing the data, the information stored is a string. What I would like to be able to do is transform each of the values in the map (Example: Frequency 1.07, 14.45, etc.) into doubles that i can then run through additional programs and run calculations with, such as an average. I have looked around online and havnt found anything that I am looking for, so im wondering if there would be a way to transform these strings into doubles using either an array, list, or any other means.
I am an intern for a tech company so I am still trying to hammer in Java and describing what I am talking about, so if there is any questions about what I am asking, please let me know and thanks in advance!
You could get a Map from the JSON file , you can also extract the values array from the Map yourmap.getvalues() , then you can parse each on of these element and case it into double
Example : Frequency: [1.07, 14.45, 24.90, 22.61, 9.86]
for ( String f : Frequency ) {
double f_double = Double.parse(f); // turns String into double
}
You can do this with another class that will store duplicate attribute values in arrays. You can simply get them through a.getValues (). This is just a concept and you should extend it as it will be convenient for you.
import java.util.*;
public class Main {
public static void main(String[] args) {
Map<String, List<Attribute>> map = new LinkedHashMap<>();
List<Attribute> attributes = new ArrayList<>();
attributes.add(new Attribute("frequency", 3.46, 5.11, 6.12));
attributes.add(new Attribute("magnitude", 3.46, 10.22, 10.54));
//and so on
map.put("idString1", attributes);
//printing double values
for (String key : map.keySet()) {
for (Attribute a : map.get(key)) {
System.out.println(a.getName() + " " +Arrays.toString(a.getValues()));
//a.getValues() to get all of doubles
}
}
}
private static class Attribute {
private String name;
private double[] values;
Attribute(String name, double... values) {
this.name = name;
this.values = values;
}
String getName() {
return name;
}
double[] getValues() {
return values;
}
}
}
The result will be:
frequency [3.46, 5.11, 6.12]
magnitude [3.46, 10.22, 10.54]
Your question:
I would like to be able to do is transform each of the String values in the
map (Example: Frequency 1.07, 14.45, etc.) into doubles and run calculations with, such as an average.
Yes, it is possible to transform your String array in a double array using Stream like below:
String[] frequencies = { "1.07", "14.45", "24.90", "22.61", "9.86" };
double[] arr = Stream.of(frequencies)
.mapToDouble(Double::parseDouble)
.toArray();
If you use the DoubleSummaryStatistics class you have already available ops like avg, sum :
String[] frequencies = { "1.07", "14.45", "24.90", "22.61", "9.86" };
DoubleSummaryStatistics statistics = Stream.of(frequencies)
.mapToDouble(Double::parseDouble)
.summaryStatistics();
System.out.println(statistics.getAverage()); //<-- 14.578
System.out.println(statistics.getMax()); //<-- 24.9

How to implement a HashMap from a string input

I have an array list which holds a bunch of activities inputted by the user, each of them have a title which is inputted by the user. I want the title to be parsed and inputted into a hashmap. Now the user can perform a search, where they can type certain keywords. I need to use a hashmap to check which activity in the array list they are referring too.
The problem i am having is that i know we cant have more than one values assosiated with a particular key. For example if the title for activity one is : football game, i want football to = 1 and game to = 1 (which indicates its related to the first activity) so that when the user types in one of those words it will pull up this activity.
You could potentially use a hash map on a list of strings. Make your key be a unique integer and then store any words associated with that integer in the string list. That way "game" could be associated with "soccer game" or "hockey game". But it depends how you associate your strings with your keys I guess.
Map<From, To> looks like it can only map to single things. That is luckily not true since To can also be a List<To> or a Set<To> or even a Map again.
Using complex types as values has some downsides though. You have to create those sets/lists per entry and handle null values.
roughly like
private Map<String, List<Integer>> activityMap = new HashMap<>();
private void add(String key, Integer value) {
List<Integer> list = activityMap.get(key);
if (list == null) {
// must create and add the list
list = new ArrayList<>();
activityMap.put(key, list);
}
list.add(value);
}
private List<Integer> getAll(String key) {
List<Integer> list = activityMap.get(key);
// simpler to use if there is never null as result.
if (list == null)
list = new ArrayList<>();
return list;
}
private void remove(String key, Integer value) {
List<Integer> list = activityMap.get(key);
if (list == null)
return;
// here should probably be list.remove(Object) - it looks confusing with Integer though
for (Iterator<Integer> iterator = list.iterator(); iterator.hasNext();) {
Integer listValue = iterator.next();
if (listValue.equals(value)) {
iterator.remove();
}
}
}
Guava Multimap is an implementation of such a structure in case libraries are an option.
I am not sure what is really want to put into hashmap, and I just assume it looks like this:
"user1" => "bascketbal = 1, footdball = 2"
"user2" => "football = 3"
"user3" => "pingpong = 1"
If so, you can use Map<String, Map<String, Integer>>, e.g:
Map userActiveData = new HashMap<String, Map<String, Integer>>();
//For each user, maybe in a loop
Map<String, Integer> active = new HashMap<String, Integer>();
active.put("football", 1);
active.put("game", 2);
userActiveData.put("user1", active);
Use a list as the value of the HashMap.
Map<String, List<String>> map = new HashMap<String, List<String>>();
Pseudo code:
Parse your title and find the keywords.
If the keyword is a new one Then
add it to the HashMap as a key, and add the Activity_id as the first element of the list.
Else (keyword is already in the HashMap) Then
add Activity_id as the next element of the corresponding list.
When you search a keyword you can return the list with all the Activity_ids that matches the keyword
Example:
Input: 1 - soccer game | 2 - badminton game
This is what HashMap will look like
~KEY~ | ~VALUES(List)~
soccer | 1
badminton | 2
game | 1,2
I think this is what you want, it supports the whole key and a only part of the activity to search, please check the output :
public class ActivityManager {
Map<String, Set<String>> map = new HashMap<String, Set<String>>();
public static void main(String[] args) {
ActivityManager testMap = new ActivityManager();
testMap.addActivity("football game");
testMap.addActivity("basketball game");
Set<String> football=testMap.getActivities("football");
Set<String> basketball=testMap.getActivities("basketball");
Set<String> game=testMap.getActivities("game");
Set<String> footballGame=testMap.getActivities("football game");
System.out.println("-------football------");
printActivities(football);
System.out.println("-------game------");
printActivities(game);
System.out.println("-------basketball------");
printActivities(basketball);
System.out.println("-------football game------");
printActivities(footballGame);
}
public void addActivity(String activity) {
String[] keyWords = activity.split(" ");// split key words, change to what you want if needed
Set<String> activities=null;
for (String key : keyWords) {
activities = map.get(key);
if (activities == null) {// do not have any activities mapped to this key yet
activities = new HashSet<String>();
map.put(key, activities);
}
activities.add(activity);// put new value into it.
}
if (keyWords.length>1){
Set<String> activitiesUsingWholeKey =map.get(activity);//get the activities using the whole word
if(activitiesUsingWholeKey==null){
activitiesUsingWholeKey=new HashSet<String>();
map.put(activity, activitiesUsingWholeKey);
}
activitiesUsingWholeKey.add(activity);
}
}
public Set<String> getActivities(String key){
return this.map.get(key);
}
private static void printActivities(Set<String> activities){
for(String activity:activities)
System.out.println(activity);
}
}
Output :
-------football------
football game
-------game------
basketball game
football game
-------basketball------
basketball game
-------football game------
football game

Create a 2d Boolean array in Java from table data

I have a .csv file of type:
Event Participant
ConferenceA John
ConferenceA Joe
ConferenceA Mary
ConferenceB John
ConferenceB Ted
ConferenceC Jessica
I would like to create a 2D boolean matrix of the following format:
Event John Joe Mary Ted Jessica
ConferenceA 1 1 1 0 0
ConferenceB 1 0 0 1 0
ConferenceC 0 0 0 0 1
I start by reading in the csv and using it to initialize an ArrayList of type:
AttendaceRecord(String title, String employee)
How can I iterate through this ArrayList to create a boolean matrix like the one above in Java?
This is the easiest way I can think of for you. This answer can certainly be improved or done in a completely different way. I'm taking this approach because you mentioned that you are not completely familiar with Map (I'm also guessing with Set). Anyway let's dive in.
In your AttendanceRecord class you are going to need the following instance variables: two LinkedHashSet and one LinkedHashMap. LinkedHashSet #1 will store all conferences and LinkedHashSet #2 will store all participants. The LinkedHashMap will store the the conferences as keys and participants list as values. The reason for this will be clear in a minute. I'll first explain why you need the LinkedHashSet.
Purpose of LinkedHashSet
Notice in your 2d array, the rows (conferences) and columns (participants) are arranged in the order they were read. Not only that, all duplicates read from the file are gone. To preserve the ordering and eliminate duplicates a LinkedHashSet fits this purpose perfectly. Then, we will have a one-to-one relationship between the row positions and the column positions of the 2d array and each LinkedHashSet via their array representation. Let's use Jhon from ConferenceA for example. Jhon will be at position 0 in the array representation of the participant Set and ConferenceA will be at position 0 in the array representation of the conference Set. Not only that, the size of each array will be used to determine the size of your 2d array (2darray[conferenceArrayLength][participantArrayLength])
Purpose of the LinkedHashMap
We need the LinkedHashMap to preserve the ordering of the elements (hence Linked). The elements will be stored internally like this.
ConferenceA :Jhon Joe Mary
ConferenceB :Jhon Ted
ConferenceC :Jessica
We will then iterate through the data structure and send each key value pair to a function which returns the position of each element from each array returned from each LinkedHashSet. As each row and column position is returned, we will add a 1 to that position in the 2d array.
Note: I used an Integer array for my example, substitute as needed.
AttendanceRecord.java
public class AttendanceRecord {
private Map<String, ArrayList> attendanceRecordMap = new LinkedHashMap<String, ArrayList>();
private Set<String> participants = new LinkedHashSet<String>();
private Set<String> conferences = new LinkedHashSet<String>();
public AttendanceRecord() {
}
public Map<String, ArrayList> getAttendanceRecordMap() {
return attendanceRecordMap;
}
public Object[] getParticipantsArray() {
return participants.toArray();
}
public Object[] getConferencesArray() {
return conferences.toArray();
}
public void addToRecord(String title, String employee) {
conferences.add(title);
participants.add(employee);
if (attendanceRecordMap.containsKey(title)) {
ArrayList<String> tempList = attendanceRecordMap.get(title);
tempList.add(employee);
} else {
ArrayList<String> attendees = new ArrayList<String>();
attendees.add(employee);
attendanceRecordMap.put(title, attendees);
}
}
}
Test.java
public class Test {
public static void main(String[] args) {
AttendanceRecord attendanceRecord = new AttendanceRecord();
//There are hardcoded. You will have to substitute with your code
//when you read the file
attendanceRecord.addToRecord("ConferenceA", "Jhon");
attendanceRecord.addToRecord("ConferenceA", "Joe");
attendanceRecord.addToRecord("ConferenceA", "Mary");
attendanceRecord.addToRecord("ConferenceB", "Jhon");
attendanceRecord.addToRecord("ConferenceB", "Ted");
attendanceRecord.addToRecord("ConferenceC", "Jessica");
int[][] jaccardArray = new int[attendanceRecord.getConferencesArray().length][attendanceRecord.getParticipantsArray().length];
setUp2dArray(jaccardArray, attendanceRecord);
print2dArray(jaccardArray);
}
public static void setUp2dArray(int[][] jaccardArray, AttendanceRecord record) {
Map<String, ArrayList> recordMap = record.getAttendanceRecordMap();
for (String key : recordMap.keySet()) {
ArrayList<String> attendees = recordMap.get(key);
for (String attendee : attendees) {
int row = findConferencePosition(key, record.getConferencesArray());
int column = findParticipantPosition(attendee, record.getParticipantsArray());
System.out.println("Row inside " + row + "Col inside " + column);
jaccardArray[row][column] = 1;
}
}
}
public static void print2dArray(int[][] jaccardArray) {
for (int i = 0; i < jaccardArray.length; i++) {
for (int j = 0; j < jaccardArray[i].length; j++) {
System.out.print(jaccardArray[i][j]);
}
System.out.println();
}
}
public static int findParticipantPosition(String employee, Object[] participantArray) {
int position = -1;
for (int i = 0; i < participantArray.length; i++) {
if (employee.equals(participantArray[i].toString())) {
position = i;
break;
}
}
return position;
}
public static int findConferencePosition(String employee, Object[] conferenceArray) {
int position = -1;
for (int i = 0; i < conferenceArray.length; i++) {
if (employee.equals(conferenceArray[i])) {
position = i;
break;
}
}
return position;
}
}
Basically you'll want to start by searching through your input strings to find each of the names (String.contains) and set a boolean array of each field name.
Then you'll make an array of those boolean arrays (or a list, whatever).
Then you simply sort through them, looking for T/F and printing corresponding messages.
I included some very rough pseudocode, assuming I am understanding your problem correctly.
// For first row
List labelStrings[];
labelStrings = {"Event", "John", "Joe", "Mary", "Ted", "Jessica"};
// For the matrix data
// List to iterate horizontally EDIT: Made boolean!
List<Boolean> strList= new ArrayList()<List>;
// List to iterate vertically
List<List> = listList new ArrayList()<List>;
/* for all the entries in AttendanceRecord (watch your spelling, OP)
for all data sets mapping title to employee
add the row data to strList[entry_num] */
for (int i = 0; i < listList.size()-1; i++)
for (int j = 0; j < labelStrings.size()-1; j++)
{
if (i == 0)
System.out.println(strList[j] + "\t\n\n");
else
{
// print listLists[i][j]
}
// iterate row by row (for each horizontal entry in the column of entries)
}
Sorry, I'm just reading through the comments now.
You'll definitely want to arrange your data in a way that is easy to iterate through. Since you have a fixed table size, you could hardcode a boolean array for each entry and then print on validation they were mapped to the event as indicated in your input string.
Try creating a hash map containing
HashMap map = new HashMap<conferenceStr, HashMap<nameStr, int>>()
As you iterate through your ArrayList, you can do something like
innerMap = map.get(conferenceStr)
innerMap.put(nameStr, 1)
of course you'll need some initialization logic, like you can check if innerMap.get(nameStr) exists, if not, iterate over every inner map and innerMap.put(nameStr, 0)
This structure can be used to generate that final 2D boolean matrix.
Elaboration edit:
ArrayList<AttendanceRecord> attendanceList = new ArrayList<AttendanceRecord>();
// populate list with info from the csv (you implied you can do this)
HashMap<String, HashMap<String, Integer>> map = new HashMap<String, HashMap<String, Integer>>();
//map to store every participant, this seems inefficient though
HashMap<String, Integer>> participantMap = new HashMap<String, Integer>();
for (AttendanceRecord record : attendanceList) {
String title = record.getTitle();
String employee = record.getEmployee();
participantMap.put(employee, 0);
HashMap<String, Integer> innerMap = map.get(title);
if (innerMap == null) {
innerMap = new HashMap<String, Integer>();
}
innerMap.put(employee, 1);
}
//now we have all the data we need, it's just about how you want to format it
for example if you wanted to just print out a table like that you could iterate through every element of map doing this:
for (HashMap<String, Integer> innerMap : map.values()) {
for (String employee : participantMap.values()) {
if (innerMap.get(employee)) {
//print 1
}
else
//print 0
}
}

Iterating through an array List and creating new ArrayLists when values are different, is this even possible?

I am fairly new to Java and I have stumbled across a problem I cannot figure out for the life of me. First let me explain what I am trying to do then I will show you the code I have so far.
I have a webservice that returns an array of arrays(which include company and lines of business strings). I wish to transform this into a string list, which I did in the first line of code below. Then I wish to Iterate through the list and every I come across a different value for company, I want to create a new ArrayList and add the associated line of business to the new list. Example output of webservice: 80,80,64,64 (this is presorted so the same companies will always be grouped together) the associated lobs would be 1,2,3,4 respectively. What I want: arraylist[0]: 1,2 arrayList[1]: 3,4
What I have so far:
List coList = Arrays.asList(coArray);
//create list of lists
List<List<String>> listOfLists = new ArrayList<List<String>>();
String cmp = "";
for (int i=0;i<coList.size();i++){//loop over coList and find diff in companies
String currentCo = ((__LOBList)coList.get(i)).getCompany();
String currentLob = ((__LOBList)coList.get(i)).getLobNum();
if(i<coArray.length-1){
String nextCo = ((__LOBList)coList.get(i+1)).getCompany();
if((currentCo.equals(nextCo))){
//do nothing companies are equal
}else{
log("NOT EQUAL"); //insert logic to create a new array??
ArrayList<String> newList = new ArrayList<String>();
// for(int j=0;j<coList.size();j++){
newList.add( ((__LOBList)coList.get(i)).getLobNum());
// }
for(int k=0; k<listOfLists.size();k++){//loop over all lists
for(int l=0;l<listOfLists.get(k).size();l++){ //get first list and loop through
}
listOfLists.add(newList);
}
}
}
}
My problem here is that it is not adding the elements to the new string array. It does correctly loop through coList and I put a log where the companies are not equal so I do know where I need to create a new arrayList but I cannot get it to work for the life of me, please help!
Yes you can do this but it's really annoying to write in Java. Note: This is a brain dead simple in a functional programming language like Clojure or Haskell. It's simply a function called group-by. In java, here's how I'd do this:
Initialize a List of Lists.
Create a last pointer that is a List. This holds the last list you've added to.
Iterate the raw data and populate into the last as long as "nothing's changed". If something has changed, create a new last.
I'll show you how:
package com.sandbox;
import java.util.ArrayList;
import java.util.List;
public class Sandbox {
public static void main(String[] args) {
ArrayList<String> rawInput = new ArrayList<String>();
rawInput.add("80");
rawInput.add("80");
rawInput.add("60");
rawInput.add("60");
new Sandbox().groupBy(rawInput);
}
public void groupBy(List<String> rawInput) {
List<List<String>> output = new ArrayList<>();
List<String> last = null;
for (String field : rawInput) {
if (last == null || !last.get(0).equals(field)) {
last = new ArrayList<String>();
last.add(field);
output.add(last);
} else {
last.add(field);
}
}
for (List<String> strings : output) {
System.out.println(strings);
}
}
}
This outputs:
[80, 80]
[60, 60]
Of course, you can do what the other guys are suggesting but this changes your data type. They're suggesting "the right tool for the job", but they're not mentioning guava's Multimap. This will make your life way easier if you decide to change your data type to a map.
Here's an example of how to use it from this article:
public class MutliMapTest {
public static void main(String... args) {
Multimap<String, String> myMultimap = ArrayListMultimap.create();
// Adding some key/value
myMultimap.put("Fruits", "Bannana");
myMultimap.put("Fruits", "Apple");
myMultimap.put("Fruits", "Pear");
myMultimap.put("Vegetables", "Carrot");
// Getting the size
int size = myMultimap.size();
System.out.println(size); // 4
// Getting values
Collection<string> fruits = myMultimap.get("Fruits");
System.out.println(fruits); // [Bannana, Apple, Pear]
Collection<string> vegetables = myMultimap.get("Vegetables");
System.out.println(vegetables); // [Carrot]
// Iterating over entire Mutlimap
for(String value : myMultimap.values()) {
System.out.println(value);
}
// Removing a single value
myMultimap.remove("Fruits","Pear");
System.out.println(myMultimap.get("Fruits")); // [Bannana, Pear]
// Remove all values for a key
myMultimap.removeAll("Fruits");
System.out.println(myMultimap.get("Fruits")); // [] (Empty Collection!)
}
}
It sounds to me like a better choice would be a Map of Lists. Let the company ID be the key in the Map and append each new item for that company ID to the List that's the value.
Use the right tool for the job. Arrays are too low level.
Create a Map<String, List<Bussiness>>
Each time you retrieve a company name, first check if the key is already in the map. If it is, retrieve the list and add the Bussiness object to it. If it is not, insert the new value when a empty List and insert the value being evaluated.
try to use foreach instead of for
just like
foreach(List firstGroup in listOfLists)
foreach(String s in firstGroup)
............
Thanks for the input everyone!
I ended up going with a list of lists:
import java.util.*;
import search.LOBList;
public class arraySearch {
/**
* #param args
*/
public static void main(String[] args) {
LOBList test = new LOBList();
test.setCompany("80");
test.setLOB("106");
LOBList test1 = new LOBList();
test1.setCompany("80");
test1.setLOB("601");
LOBList test2 = new LOBList();
test2.setCompany("80");
test2.setLOB("602");
LOBList test3 = new LOBList();
test3.setCompany("90");
test3.setLOB("102");
LOBList test4 = new LOBList();
test4.setCompany("90");
test4.setLOB("102");
LOBList test5 = new LOBList();
test5.setCompany("100");
test5.setLOB("102");
LOBList BREAK = new LOBList();
BREAK.setCompany("BREAK");
BREAK.setLOB("BREAK");
BREAK.setcompany_lob("BREAK");
// create arraylist
ArrayList<LOBList> arlst=new ArrayList<LOBList>();
// populate the list
arlst.add(0,test);
arlst.add(1,test1);
arlst.add(2,test2);
arlst.add(3,test3);
arlst.add(4,test4);
arlst.add(5,test5);
//declare variables
int idx = 0;
String nextVal = "";
//loops through list returned from service, inserts 'BREAK' between different groups of companies
for(idx=0;idx<arlst.size();idx++){
String current = arlst.get(idx).getCompany();
if(idx != arlst.size()-1){
String next = arlst.get(idx+1).getCompany();
nextVal = next;
if(!(current.equals(next))){
arlst.add(idx+1,BREAK);
idx++;
}
}
}
//add last break at end of arrayList
arlst.add(arlst.size(),BREAK);
for(int i=0;i<arlst.size();i++){
System.out.println("co:" + arlst.get(i).getCompany());
}
//master array list
ArrayList<ArrayList<LOBList>> mymasterList=new ArrayList<ArrayList<LOBList>>();
mymasterList = searchListCreateNewLists(arlst);
//print log, prints all elements in all arrays
for(int i=0;i<mymasterList.size();i++){
for(int j=0;j<mymasterList.get(i).size();j++){
System.out.println("search method: " + mymasterList.get(i).get(j).getCompany());
}
System.out.println("end of current list");
}
}
//method to loop over company array, finds break, creates new array list for each company group,
//adds this to a list of lists(masterList)
public static ArrayList<ArrayList<LOBList>> searchListCreateNewLists(ArrayList<LOBList> list){
ArrayList<ArrayList<LOBList>> masterList=new ArrayList<ArrayList<LOBList>>();
int end = 0;
int start = 0;
int index = 0;
for(int i=0;i<list.size();i++){
if(list.get(i).getCompany().equals("BREAK")){
end = i;//end is current index
masterList.add(new ArrayList<LOBList>());
for(int j = start;j<end;j++){
masterList.get(index).add(list.get(j));
}
index++;
start = i+1;
}
}
return masterList;
}
}
The output is:
search method: 80
search method: 80
search method: 80
end of current list
search method: 90
search method: 90
end of current list
search method: 100
end of current list
So all company LOBList objects with Company: 80, are grouped together in a list, as are 90 and 100.
To iterate through the list you can use
ListIterator litr = coList.listIterator();
while(litr.hasNext()){
}

Compare two Maps in Java

I want to be able to compare two arrays (which will hold values of the same XML but different ages, so i can see if any changed have been made). I have two arrays, one contains attributes and values of the older XML line that i have parsed, and the other contains attributes and latest version of that same XML line that in have parsed.
Example:
Array1:
rect x="220.00"
width="300.00"
id="rect_1"
y="180.00"
height="280.00"
Array2:
rect x="300.00"
width="400.00"
id="rect_1"
height="280.00"
info = "description"
etc etc
So here, the changes would be:
The rect x attribute has changed from 220 (array1) to 300 (array2)
the width attribute has changed from 300 (array1) to 400(array2)
Array2 has gained an attribute called info
y has been removed from array2
How would I compare two arrays and display results like that? Basically I want it to show changes and differences.
Heres the code i tried:
Collection<String> listOne = Arrays.asList(array1);
Collection<String> listTwo = Arrays.asList(array);
Collection<String> similar = new HashSet<String>( listOne );
Collection<String> different = new HashSet<String>();
different.addAll( listOne );
different.addAll( listTwo );
similar.retainAll( listTwo );
different.removeAll( similar );
resultsBuff.append("\nDifferences: \n"+ different + "\n\nChanges: \n" + similar);
This code didn't quite do what I wanted it to do (as described earlier).
You have no choice but to loop through both Arrays.
I would loop through the attributes, split key and value and build a HashMap for each array.
Map<String, String> map1 = new HashMap<String, String>()
for (String attribute : array1) {
String[] splitted = attribute.split("=");
map1.put(splitted[0], splitted[1]);
}
Do the same to create map2.
Map<String, String> map2 = new HashMap<String, String>();
...
Loop through first map and verify if key/value is different or exist in map2 to detected attribute removal.
for (String key : map1.keySet()) {
if (!map2.containsKey(key)) {
System.out.println(key + "has been removed from Array2" )
} else if (!map1.get(key).equals(map2.get(key)) {
System.out.println(key + "attribute has changed from " + map1.get(key) + " to " + map2.get(key) );
}
}
Loop through map2 to detect new attributes
for (String key : map2.keySet()) {
if (!map1.containsKey(key)) {
System.out.println(key + "has been added to Array2" );
}
Hope this helps!
I would use a HashMap instead of an array, because it is better suited for this kind of key/value structures:
map.put("rect x","220.00");
map.put("width","300.00");
...
Build 2 hashmaps from the 2 arrays, and compare them:
if(!map1.equals(map2)) { //something has changed
//loop over keys and compare values
}
I would create an object holding this information and implement a custom equals. You dont need to use an array as a data structure. You can use an object.
For example
public class MyObject{
private double rect_x;
private double width;
private double id;
private double y;
private double height
//usual getters and setters
//implement hashcode
//implemement equals eg
public boolean equals (Object o) {
if (!(o instanceof MyObject)){
return false;
}
MyObject that= MyObject.class.cast(o);
return this.width == that.width && this.id == that.id etc etc
}
}
You already have maps? or they are just arrays?
I mean, are those "labels" implicit or not?
If they're not and you actually have two Map, you could easily do something like:
for(Map.Entry<String,String> pair : array1){
String key = pair.getKey();
String value = pair.getValue();
if(!array2.containsKey(key)){
System.out.println("Array2 lost attribute " + key);
}else{
String value2 = array2.get(key);
if(!value.equals(value2){
System.out.println("The '"+key+"' attribute has changed from "+value+" to "+ value2 ;
}
array2.remove(key);
}
}
for(Map.Entry<String,String> pair : array2){
String key = pair.getKey();
System.out.println("Array2 gained attribute " + key);
}
If you don't have the explicit labels, you just create another mapping before this code and use it to build two maps....

Categories