Sorting Duplicate Keys with respective values in java [closed] - java

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I need to sort house numbers in my project. But I did not get exact logic for my problem.
The list is:
9-11, 9-01, 10-02, 10-01, 2-09, 3-88, 9-03
I need to sort the above list as:
2-09, 3-88, 9-01, 9-03, 9-11, 10-01, 10-02
I need the output like above.
Can any one help me in Java coding?

The logic is simple here:
209 < 388 < 901 < 902 < 911 < 1001 < 1002
One possible solution is:
String[] input = {"9-11", "9-01", "10-02", "10-01", "2-09", "3-88", "9-03"};
Map<Integer, String> map = new TreeMap<Integer, String>();
for (String s : input) {
map.put(Integer.valueOf(s.replace("-", "")), s);
}
TreeSet<Integer> set = new TreeSet<Integer>(map.keySet());
String[] output = new String[input.length];
int i = 0;
for (Integer key : set) {
output[i++] = map.get(key);
}
If you want to support more flexible formats, you can implement Comparable to define exactly comparisson rules. Let's take a look at modified example:
class HouseNumber implements Comparable<HouseNumber>{
private Integer house;
private Integer flat;
private HouseNumber(String s) {
String[] fields = s.split("-");
house = Integer.valueOf(fields[0]);
flat = Integer.valueOf(fields[1]);
}
#Override
public int compareTo(HouseNumber o) {
if (house.compareTo(o.house) == 0) {
return flat.compareTo(o.flat);
}
return house.compareTo(o.house);
}
}
String[] input = {"9-11", "9-01", "10-02", "10-01", "2-09", "3-88", "9-03"};
Map<HouseNumber, String> map = new TreeMap<HouseNumber, String>();
for (String s : input) {
map.put(new HouseNumber(s), s);
}
TreeSet<HouseNumber> set = new TreeSet<HouseNumber>(map.keySet());
String[] output = new String[input.length];
int i = 0;
for (HouseNumber key : set) {
output[i++] = map.get(key);
}

Simply remove "-" from the list
9-11, 9-01, 10-02, 10-01, 2-09, 3-88, 9-03
becomes
911, 901, 1002, 1001, 209, 388, 903
Sort
209, 388, 901, 903, 911, 1001, 1002
Place the "-" back, skipping 2 places from the back
2-09, 3-88, 9-01, 9-03, 9-11, 10-01, 10-02
Simple as that !

Do like this
Your Comparator
class SimpleComparator implements Comparator<String> {
#Override
public int compare(String o1, String o2) {
String [] o1sub = o1.split("-");
String [] o2sub = o2.split("-");
int value1 = Integer.parseInt(o1sub[0]);
int value2 = Integer.parseInt(o2sub[0]);
if(value1!=value2){
return new Integer (value1).compareTo(value2);
}
int value3 = Integer.parseInt(o1sub[1]);
int value4 = Integer.parseInt(o2sub[1]);
if(value3!=value4){
return new Integer(value3).compareTo(value4);
}
return 0;
}
}
Data
String [] array ={"9-11","9-01","10-02","10-01","2-09","3-88","9-03"};
Sorting
Arrays.sort(array,new SimpleComparator());
System.out.println(Arrays.toString(array));
OutPut
[2-09, 3-88, 9-01, 9-03, 9-11, 10-01, 10-02]

I have share you solved problem, May be it will help you to get exactly you want...
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class HouseNo {
public HouseNo(String house, String block) {
this.houseno = house;
this.blockno = block;
}
private String houseno;
private String blockno;
public String getHouseno() {
return houseno;
}
public void setHouseno(String houseno) {
this.houseno = houseno;
}
public String getBlockno() {
return blockno;
}
public void setBlockno(String blockno) {
this.blockno = blockno;
}
public static class SortByHouseNo implements Comparator<HouseNo> {
#Override
public int compare(HouseNo o1, HouseNo o2) {
Integer first = Integer.valueOf(o1.getHouseno());
Integer second = Integer.valueOf(o2.getHouseno());
Integer f1 = Integer.valueOf(o1.getBlockno());
Integer f2 = Integer.valueOf(o2.getBlockno());
if (first.compareTo(second) == 0) {
return f1.compareTo(f2);
}
return first.compareTo(second);
}
}
#Override
public String toString() {
return "House -> " + this.getHouseno() + "-" + this.getBlockno();
}
public static void main(String[] args) {
// Final
String houseList[] = { "9-11", "9-01", "10-02", "10-01", "2-09",
"3-88", "9-03", "9-3" };
HouseNo house = null;
ArrayList<HouseNo> sortedList = new ArrayList<>();
for (String string : houseList) {
String h = string.substring(0, string.indexOf('-'));
String b = string.substring(string.indexOf('-') + 1);
house = new HouseNo(h, b);
sortedList.add(house);
}
System.out.println("Before Sorting :: ");
for (HouseNo houseNo : sortedList) {
System.out.println(houseNo);
}
Collections.sort(sortedList, new SortByHouseNo());
System.out.println("\n\nAfter Sorting HouseNo :: ");
for (HouseNo houseNo : sortedList) {
System.out.println(houseNo);
}
}
}
Updated solution......

Related

How to add/append new rows from Array1[][] that do not exist in Array2[][] to Array2[][] in Java?

I came across a problem where one needs to check for rows in Array1 that are not in Array2 and append it at the end of Array2 in Java. The rows that are common with regard to the first column i.e. name can be skipped. In the below example, the rows in firstarray with "Nick" and "Bruce" should be appended at the end of secondarray.
I have edited the arrays again slightly to get more clarity.
String firstarray[][] = {
{"John","04-Feb-1982","Economics","Leeds"},
{"Mathias","08-Jan-1985","Arts","London"},
{"Nick","09-06-1974","History","Johanesburg"},
{"Bruce","13-08-1975","Philosophy","Seattle"}};
String secondarray[][] = {
{"Adam","01-Dec-1980","Commerce","New York"},
{"John","04-Feb-1982","Economics","Leeds"},
{"Mathias","08-Jan-1985","Arts","London"}};
The solution should be like:
secondarray[][]:
{"Adam","01-Dec-1980","Commerce","New York"},
{"John","04-Feb-1982","Economics","Leeds"},
{"Mathias","08-Jan-1985","Arts","London"},
{"Nick","09-06-1974","History","Johanesburg"},
{"Bruce","13-08-1975","Philosophy","Seattle"}}
Collect the names of the second array to a set, iterate over your first array and filter out those elements which are in the set and collect the result in a third array (or any other collection). Append this collection to your second array.
public static void main(String[] args){
String firstarray[][] = {
{"Adam","01-Dec-1980","Commerce","Kansas"},
{"John","04-Feb-1982","Economics","Leeds"},
{"Mathias","08-Jan-1985","Arts","London"},
{"Nick","09-06-1974","History","Johanesburg"},
{"Bruce","13-08-1975","Philosophy","Seattle"}};
String secondarray[][] = {
{"Adam","01-Dec-1980","Commerce","Kansas"},
{"John","04-Feb-1982","Economics","Leeds"},
{"Mathias","08-Jan-1985","Arts","London"},
{"Sujay Muramalla","08-Jan-1985","Arts","London"}};
//collect names of second array to set
Set<String> secondSet = Arrays.stream(secondarray).map(e -> e[0]).collect(Collectors.toSet());
//stream over your first array and keep only those which are not in the above set
String[][] third = Arrays.stream(firstarray)
.filter(e -> !secondSet.contains(e[0]))
.toArray(String[][]::new);
//stream over second and delta (third array) and collect to a result array
String[][] result = Stream.concat(Arrays.stream(secondarray), Arrays.stream(third))
.toArray(String[][]::new);
//output
Arrays.stream(result).forEach(e ->{
System.out.println(Arrays.toString(e));
});
}
You should not be using arrays for this. Much better to use libraries already doing the job.
Even if the data you receive is already that way, you can converted first to a Map<String, Person> it will be more efficient, and readable code.
With arrays or many solutions not using some hashing system, you end up with exponential complexity O(n2), so not efficient.
Convert at least secondarray
Map<String, Person> secondMap = new HashMap();
for(String [] row : secondarray){
secondMap.put(row[0], new Person(row[0], row[1], row[2], row[3]));
}
Then put in the map if not already there
for(String[] row : firstarray){
if(!secondMap.containsKey(row[0])){
secondMap.put(row[0], new Person(row[0], row[1], row[2], row[3]));
}
}
Where Person class could be simply defined as
private static class Person{
public Person(String name, String birth, String area, String city){
this.name = name;
this.birth = birth;
this.area = area;
this.city = city;
}
String name;
String birth;
String area;
String city;
}
if you want check more than the first element of the array you can use nopens solution and replace e[0] with Arrays.toString(e).
A cleaner way if this is possible for you, is to use a list with a object and use a id for checking equals or override the hashcode function of the customer object.
You can also check for name and birth like that:
class Customer {
private String name;
private String birth;
private String type;
private String location;
public Customer(String name, String birth, String type, String location) {
this.name = name;
this.birth = birth;
this.type = type;
this.location = location;
}
#Override
public String toString() {
return "Customer [name=" + name + ", birth=" + birth + ", type=" + type + ", location=" + location + "]";
}
}
List<Customer> firstList = new ArrayList<Customer>();
firstList.add(new Customer("Adam", "01-Dec-1980", "Commerce", "Kansas"));
firstList.add(new Customer("John", "04-Feb-1982", "Economics", "Leeds"));
firstList.add(new Customer("Mathias", "08-Jan-1985", "Arts", "London"));
firstList.add(new Customer("Nick", "09-06-1974", "History", "Johanesburg"));
firstList.add(new Customer("Bruce", "13-08-1975", "Philosophy", "Seattle"));
List<Customer> secondList = new ArrayList<Customer>();
secondList.add(new Customer("Adam", "01-Dec-1980", "Commerce", "Kansas"));
secondList.add(new Customer("John", "04-Feb-1982", "Economics", "Leeds"));
secondList.add(new Customer("Mathias", "08-Jan-1985", "Arts", "London"));
for (Customer customer : firstList) {
if (containsNameAndBirth(secondList, customer) == false) {
secondList.add(customer);
}
}
for (Customer customer : secondList) {
System.out.println(customer);
}
}
public static boolean containsNameAndBirth(final List<Customer> list, final Customer customer) {
return list.stream().filter(o -> o.name.equals(customer.name) && o.birth.equals(customer.birth)).findFirst()
.isPresent();
}
EDIT 1 - Using Custom Class
I suggest you to always use List over Array.
import java.time.*;
import java.util.*;
import java.util.stream.Collectors;
public class Main {
static class Person {
public String name;
public String birthDate;
public String field;
public String city;
public static Person fromArray(String[] data) {
Person p = new Person();
if (data.length == 4) {
p.name = data[0];
p.birthDate = data[1];
p.field = data[2];
p.city = data[3];
} else {
// Handle me
}
return p;
}
#Override
public String toString() {
return new StringBuilder("[").append(name)
.append(",").append(birthDate)
.append("] learns ").append(field)
.append(" at ").append(city).toString();
}
}
public static void main(String[] args) {
String firstArray[][] = {
{"Adam","01-Dec-1980","Commerce","Kansas"},
{"John","04-Feb-1982","Economics","Leeds"},
{"Mathias","08-Jan-1985","Arts","London"},
{"Nick","09-06-1974","History","Johanesburg"},
{"Bruce","13-08-1975","Philosophy","Seattle"}};
String secondArray[][] = {
{"Adam","01-Dec-1980","Commerce","Kansas"},
{"John","04-Feb-1982","Economics","Leeds"},
{"Mathias","08-Jan-1985","Arts","London"}};
List<Person> finalList = getFinalList(firstArray, secondArray);
// Display
System.out.println(finalList);
}
public static List<Person> getFinalList(String[][] arr1, String[][] arr2) {
// First cast to Lists of persons
List<Person> firstList = Arrays.asList(arr1).stream().map(Person::fromArray).collect(Collectors.toList());
List<Person> secondList = Arrays.asList(arr2).stream().map(Person::fromArray).collect(Collectors.toList());
// Get names in secondList
Set<String> existingNames = secondList.stream().map(p -> p.name).collect(Collectors.toSet());
System.out.println("Names: "+ existingNames);
firstList.forEach(person -> {
if (! existingNames.contains(person.name)) {
secondList.add(person);
}
});
return secondList;
}
}
I upvoted nopens solutions cause it is nice one
Here another that uses maps and makes use of a logic of skipping common keys using removeAll on the keySet of the map, which was a functional method existing befor Java turned "more" functional
static public <T> Map<T,T[]> arrayToMap(T[][] array, int i) {
return Arrays.stream(array).collect(Collectors.toMap(e -> e[i], e -> e));
}
public static void main(String[] args){
String firstarray[][] = {
{"Adam","01-Dec-1980","Commerce","Kansas"},
{"John","04-Feb-1982","Economics","Leeds"},
{"Mathias","08-Jan-1985","Arts","London"},
{"Nick","09-06-1974","History","Johanesburg"},
{"Bruce","13-08-1975","Philosophy","Seattle"}};
String secondarray[][] = {
{"Adam","01-Dec-1980","Commerce","Kansas"},
{"John","04-Feb-1982","Economics","Leeds"},
{"Mathias","08-Jan-1985","Arts","London"},
{"Sujay Muramalla","08-Jan-1985","Arts","London"}};
Map<String,String[]> firstMap = arrayToMap(firstarray, 0);
Map<String,String[]> secondMap = arrayToMap(secondarray, 0);
secondMap.keySet().removeAll(firstMap.keySet());
firstMap.putAll(secondMap);
String[][] result = firstMap.values().stream().toArray(String[][]::new);
//output
Arrays.stream(result).forEach(e ->{
System.out.println(Arrays.toString(e));
});
}
sidenote: in arrayToMap you can choose which column you use as key.
And the logic could be even reduced to this 3 lines:
Map<String,String[]> firstMap = arrayToMap(firstarray, 0);
firstMap.putAll(arrayToMap(secondarray, 0));
String[][] result = firstMap.values().stream().toArray(String[][]::new);
since inserting a value with the same key overwrites the existing one and you get the same if the values are the same in case of equal keys.
A simple an efficient way to do it (if you don't care about ordering) is the following:
Time complexity: O(nlog(n))
Space complexity: O(n+m)
import java.util.Arrays;
public class Main {
public static void main(String ... args) {
String firstarray[][] = {
{"John","04-Feb-1982","Economics","Leeds"},
{"Mathias","08-Jan-1985","Arts","London"},
{"Nick","09-06-1974","History","Johanesburg"},
{"Bruce","13-08-1975","Philosophy","Seattle"}};
String secondarray[][] = {
{"Adam","01-Dec-1980","Commerce","Kansas"},
{"John","04-Feb-1982","Economics","Leeds"},
{"Mathias","08-Jan-1985","Arts","London"}};
String result [][] = new String[firstarray.length
+ secondarray.length][firstarray[0].length];
// sort firstarray
java.util.Arrays.sort(firstarray, new java.util.Comparator<String[]>() {
public int compare(String [] a, String[] b) {
return a[0].compareTo(b[0]);
}
});
//sort secondarray
java.util.Arrays.sort(secondarray, new java.util.Comparator<String[]>() {
public int compare(String [] a, String[] b) {
return a[0].compareTo(b[0]);
}
});
int i = 0, j=0, k=0, cmp ;
for ( ;i < secondarray.length && j< firstarray.length;) {
cmp = firstarray[i][0].compareTo(secondarray[j][0]);
if(cmp ==0) {
System.arraycopy(firstarray[i], 0, result[k++], 0, 4);
i++; j++;
}else if( cmp <0){
System.arraycopy(firstarray[i], 0, result[k++], 0, 4);
i++;
} else {
System.arraycopy(secondarray[j], 0, result[k++], 0, 4);
j++;
}
}
// copy the remaining if any from firstarray to the result
for (; i < firstarray.length; i++) {
System.arraycopy(firstarray[i], 0, result[k++], 0, 4);
}
// copy the remaining if any from secondarray to the result
for (; j < secondarray.length; j++) {
System.arraycopy(secondarray[j], 0, result[k++], 0, 4);
}
//resize it
secondarray = Arrays.copyOf(result, k);
// just print the secondarray
for (int x = 0; x < secondarray.length; x++) {
for (int y = 0; y < 4; y++) {
System.out.print(secondarray[x][y] + ",");
}
System.out.println("");
}
}
}

Comparable Sort Numbers Java [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 3 years ago.
Improve this question
This is my complete program
public class Client {
public static void main(String args[]) {
ArrayList < Student > studentsList = new ArrayList <> ();
Student std1 = new Student("Ram","01-01-1981","1203");
Student std2 = new Student("Raj","01-01-1981","1204");
Student std3 = new Student("Hanish","01-01-1981","403");
Student std4 = new Student("Hanish","01-01-1981","");
studentsList.add(std1);
studentsList.add(std2);
studentsList.add(std3);
studentsList.add(std4);
Collections.sort(studentsList);
System.out.println(studentsList);
}
}
When i print i see that the GL Number is not coming in acsending Order
The problem is that you are comparing the String GlNumber, not the number it represents. You state in the code that GLNumber is alphanumeric, so I think it's fair to say that your comparator works to spec.
"1204" < "403"
1204 > 403
On a side note: why store studentDOJ as String too? Seems like it should be java.time.LocalDate.
This happens because your glNumber is in String and You want numerical ascending.
You can understand the problem using the following examples
Sample 1
List stringList = Arrays.asList("1","2","10");
Collections.sort(stringList);
System.out.println(stringList);
// Returns [1, 10, 2]
Sample 2
List numberList = Arrays.asList(1,2,10);
Collections.sort(numberList);
System.out.println(numberList);
// Returns [1, 2, 10]
Now, the solution to your problem. Try below compareTo(...)
#Override
public int compareTo(Student other) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
return Comparator.comparing((Student student) -> LocalDate.parse(student.getStudentDOJ(), formatter)).reversed()
.thenComparingLong(student -> student.getGlNumber().length() == 0 ? 0 : Long.valueOf(student.getGlNumber()))
.compare(this, other);
}
create studentDateComparator which implements Comparator as follows:
#Override
public int compare(Student a, Student b) {
// ascending
// int r = a.getStudentDOJ().compareTo(b.getStudentDOJ());
// decending
int r = b.getStudentDOJ().compareTo(a.getStudentDOJ());
if (r == 0) {
r = comparison(a, b);
}
return r;
}
public int comparison(Student a, Student b) {
String s1 = a.getGlNumber();
String s2 = b.getGlNumber();
String[] pt1 = s1.split("((?<=[a-z])(?=[0-9]))|((?<=[0-9])(?=[a-z]))");
String[] pt2 = s2.split("((?<=[a-z])(?=[0-9]))|((?<=[0-9])(?=[a-z]))");
// pt1 and pt2 arrays will have the string split in alphabets and numbers
int i = 0;
if (Arrays.equals(pt1, pt2))
return 0;
else {
for (i = 0; i < Math.min(pt1.length, pt2.length); i++)
if (!pt1[i].equals(pt2[i])) {
if (!isNumber(pt1[i], pt2[i])) {
if (pt1[i].compareTo(pt2[i]) > 0)
return 1;
else
return -1;
} else {
int nu1 = Integer.parseInt(pt1[i]);
int nu2 = Integer.parseInt(pt2[i]);
if (nu1 > nu2)
return 1;
else
return -1;
}
}
}
if (pt1.length > i)
return 1;
else
return -1;
}
private static Boolean isNumber(String n1, String n2) {
// TODO Auto-generated method stub
try {
int nu1 = Integer.parseInt(n1);
int nu2 = Integer.parseInt(n2);
return true;
} catch (Exception x) {
return false;
}
}
}
Here is your given class:
private String studentName;
private String studentDOJ;
private String GlNumber; // alphanumeric
public Student(String studentName, String date, String glNumber) {
super();
this.studentName = studentName;
this.studentDOJ = date;
GlNumber = glNumber;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
public String getStudentDOJ() {
return studentDOJ;
}
public void setStudentDOJ(String studentDOJ) {
this.studentDOJ = studentDOJ;
}
public String getGlNumber() {
return GlNumber;
}
public void setGlNumber(String glNumber) {
GlNumber = glNumber;
}
#Override
public String toString() {
return "Student [studentName=" + studentName + ", studentDOJ=" + studentDOJ + ", GlNumber="
+ GlNumber + "]";
}
#Override
public int compareTo(Student arg0) {
return 0;
}
}
And your the main method will be like:
public static void main(String args[]) throws ParseException {
ArrayList<Student> studentsList = new ArrayList<>();
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");
Student std1 = new Student("Ram", "01-01-1981", "1203");
Student std2 = new Student("Raj", "01-01-1981", "1204");
Student std3 = new Student("Hanish", "01-01-1981", "403");
Student std4 = new Student("Hanish", "01-01-1981", "");
studentsList.add(std1);
studentsList.add(std2);
studentsList.add(std3);
studentsList.add(std4);
System.out.println(studentsList);
Collections.sort(studentsList, new studentDateComparator());
System.out.println(studentsList);
}
}
Use This to get your desired solution.
int extractInt(String s) {
String num = s.replaceAll("\\D", "");
// return 0 if no digits found
return num.isEmpty() ? 0 : Integer.parseInt(num);
}
#Override
public int compareTo(Student other) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
return Comparator.comparing((Student student) -> LocalDate.parse(student.getStudentDOJ(), formatter)).reversed()
.thenComparing(student -> extractInt(student.getGlNumber()))
.compare(this, other);
}

Java Collections - Print interstates sorted by population, city and state

I am working on a problem I came across in an interview.
Input contains Population|City|State|Interstates list
Output needs to be sorted in descending order by population first, then alphabetically by city and state, and then the interstates need to be sorted in ascending order too.
Sample input:
27|Chicago|Illinois|I-94;I-90;I-88;I-57;I-55
83|New York|New York|I-78;I-95;I-87;I-80
15|Phoenix|Arizona|I-10;I-17;I-8
15|Philadelphia|Pennsylvania|I-95;I-76
Sample output:
83
New York, New York
Interstates: I-78, I-80, I-87, I-95
27
Chicago, Illinois
Interstates: I-55, I-57, I-88, I-90, I-94
15
Philadelphia, Pennsylvania
Interstates: I-76, I-95
Phoenix, Arizona
Interstates: I-8, I-10, I-17
Here's my approach so far. I am currently stuck in the if block where I've added a comment. I am not sure if I am going in the right direction. I am looking for a hint to take the right approach here.
Scanner sc = new Scanner(System.in);
String line;
List<String> al = new ArrayList<>();
//Outer map sorts reverse by population, inner map1 sorts by city, inner
map2 sorts by state
Map<Integer, Map<String, Map<String, String>>> outerMap = new TreeMap<>
(Collections.reverseOrder());
Map<String, Map<String, String>> innerMap1 = new TreeMap<>();
Map<String, String> innerMap2 = new TreeMap<>();
while(sc.hasNextLine() && (line = sc.nextLine()).length()!=0) {
//Ignore if input contains this character
if(line.contains("#")) {
line = sc.nextLine();
}
al.add(line);
}
for(int i = 0; i < al.size(); i++) {
int outerMapKey = Integer.parseInt(al.get(i).split("\\|")[0]);
String innerMap1Key = al.get(i).split("\\|")[1];
String innerMap2Key = al.get(i).split("\\|")[2];
String value = al.get(i);
outerMap.get(outerMapKey);
if(outerMap.containsKey(outerMapKey)) {
innerMap1 = outerMap.get(outerMapKey);
/* Logic to put values in inner maps
This is going to get very convoluted, not sure if I have the
right approach
*/
}
else {
innerMap1 = new TreeMap<>();
innerMap2 = new TreeMap<>();
innerMap2.put(innerMap2Key, value);
innerMap1.put(innerMap1Key, innerMap2);
outerMap.put(outerMapKey, innerMap1);
}
}
Thank you for all your help so far. I am posting my code (working now) based on feedback here. Please take a look and suggest how it can be improved.
public static void main(String[] args) {
Map<String, List<PopulationByCityState>> map = readAndProcessInput();
printSortedOutput(map);
}
private static Map<String, List<PopulationByCityState>> readAndProcessInput() {
Map<String, List<PopulationByCityState>> map = readInput();
sortByPopulationCityAndState(map);
return map;
}
private static Map<String, List<PopulationByCityState>> readInput() {
System.out.println("Enter input:");
Scanner sc = new Scanner(System.in);
String line;
Map<String, List<PopulationByCityState>> map = new TreeMap<>(Collections.reverseOrder());
while (sc.hasNextLine() && (line = sc.nextLine()).length() != 0) {
if (line.contains("#")) {
line = sc.nextLine();
}
populateMap(line, map);
}
return map;
}
private static void populateMap(String line, Map<String, List<PopulationByCityState>> map) {
String[] s = line.split("\\|");
String[] is = s[3].split(";");
String key = s[0];
PopulationByCityState p = new PopulationByCityState();
p.setPopulation(Long.parseLong(s[0]));
p.setCity(s[1]);
p.setState(s[2]);
List<String> interstates = new ArrayList<>();
for (String aString : is) {
interstates.add(aString);
}
sortInterstates(interstates);
p.setInterstates(interstates);
if (map.containsKey(key)) {
map.get(key).add(p);
} else {
List<PopulationByCityState> al = new ArrayList<>();
al.add(p);
map.put(key, al);
}
}
private static void sortInterstates(List<String> interstates) {
Collections.sort(interstates, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
int n1 = Integer.parseInt(o1.split("-")[1]);
int n2 = Integer.parseInt(o2.split("-")[1]);
return n1 - n2;
}
});
}
private static void sortByPopulationCityAndState(Map<String, List<PopulationByCityState>> map) {
for (Map.Entry entry : map.entrySet()) {
List<PopulationByCityState> list = (List<PopulationByCityState>) entry.getValue();
Collections.sort(list, new Comparator<PopulationByCityState>() {
#Override
public int compare(PopulationByCityState o1, PopulationByCityState o2) {
int c;
c = (int) (o2.getPopulation() - o1.getPopulation());
if (c == 0) {
c = o1.getCity().compareTo(o2.getCity());
}
if (c == 0) {
c = o1.getState().compareTo(o2.getState());
}
return c;
}
});
}
}
private static void printSortedOutput(Map<String, List<PopulationByCityState>> map) {
for (Map.Entry<String, List<PopulationByCityState>> entry : map.entrySet()) {
System.out.println(entry.getKey());
System.out.println();
List<PopulationByCityState> list = entry.getValue();
for (PopulationByCityState p : list) {
System.out.println(p.getCity() + ", " + p.getState());
List<String> interstates = p.getInterstates();
System.out.print("Interstates: ");
int s = 0;
for (String is : interstates) {
s++;
System.out.print(is);
if (s != interstates.size()) {
System.out.print(", ");
}
}
System.out.println();
System.out.println();
}
}
}
Your approach relies on over complicated and not meaningful structure and also uses a Comparator that will only sort the first level of the map :
Map<Integer, Map<String, Map<String, String>>> outerMap = new TreeMap<>
(Collections.reverseOrder());
A finer approach could rely on using a class that represents each individual information that you need to represent a population for a state : PopulationForState
Here is a very simple representation of it (that is of course improvable but that should help you to understand the logic) :
public class PopulationForState{
private long population;
private String city;
private String state;
private List<String> interstates;
...
// getters
}
Add instances of them in a List and use a comparator that sorted them in descending order by population first, then alphabetically by city and state.
The interstates field may be sorted independently or directly during the sort of outer elements.
You could provide a sort method in PopulationForState, for example sortInnerStates() that sorts them in ascending order.
Personally, I would make it independently to keep the processing less coupled between.
So you could write something like :
List<PopulationForState> populationForStates = new ArrayList<>();
populationForStates.add(new PopulationForState(...));
populationForStates.add(new PopulationForState(...));
Collection.sort(populationForStates, Comparator.comparing(PopulationForState::population).reversed()
.thenComparing(PopulationForState::getCity)
.thenComparing(PopulationForState::getState);
populationForStates.stream()
.forEach(PopulationForState::sortInnerStates);
If you have a structure such the one posted in above post:
public class PopulationForState{
public long population;
public String city;
public String state;
public List<String> interstates;
//Do encapsulate
}
You can sort it with one comparator:
Collections.sort(populatisForStates, new Comparator<PopulationForState>(){
public int compare(PopulationForState first, PopulationForState scnd) {
int compare = first.population - scnd.population;
if(compare != 0) return compare;
compare = first.city.compareTo(scnd.city);
if(compare != 0) return compare;
return first.state.compareTo(scnd.state);
}
});
Sorting Interstates is similar and you just need to use Collections.sort(interstates) on each instance.

Java HashMap custom Object

Example:
d1 = "the sky is blue"
d2 = "the car is blue"
Key Value
the [<d1,1>,<d2,1>]
sky [<d1,1>]
is [<d1,1>,<d2,1>]
blue [<d1,1>,<d2,1>]
car [<d2,1>]
Where:
key = String
ex:
<d1,1>
d1 = Document id
1 = How many times the word apear on file
I created a document type object with the docid variables and frequency.
public class Documento {
private final int docid;
private final int frequencia;
public Documento(int docid, int frequencia) {
this.docid = docid;
this.frequencia = frequencia;
}
public int getDocid() {
return docid;
}
public int getFrequencia() {
return frequencia;
}
#Override
public boolean equals(Object o) {
if ((o instanceof Documento) && docid == ((Documento) o).docid && frequencia == ((Documento) o).frequencia) {
return true;
}
return false;
}
And the dictionary class that is a hashmap with
public class Dicionario {
public Map<String, Documento> indice = new HashMap<>();
public void InsereDicionario(String palavra, int docid) {
int cont = indice.containsKey(palavra) ? indice.get(palavra).getFrequencia() : 0;
indice.put(palavra, new Documento(docid, cont + 1));
}
public int frequencia(String palavra) {
return indice.get(palavra).getFrequencia();
}
public void criaDicionario(String entrada) {
String[] palavras = entrada.split("\\s+");
for (int i = 0; i < palavras.length; i++) {
InsereDicionario(palavras[i], 1);
}
}
public void ListaPalavras(){
for(String key:indice.keySet()){
System.out.println("");
}
}
But what I really need the dictionary is a list of documents , and I do not know how to do this , someone could help me ?
or is there an easier way to do this ?
If you need a list of documents, why not create one? With Java8 this becomes even more convenient:
For example:
public Map<String, List<Documento>> indice = new HashMap<>();
//register new word
indice.putIfAbsent(palavra, new ArrayList<>());
//add additional occurence
indice.get(palavra).add(documento);
//get frequency
int frequencia = indice.get(palavra)
.stream()
.map(d -> d.getFrequencia())
.reduce(0, (s, i) -> s + i);
An alternative would be to use Guava's Multimap, see here
Map<String, List<Documento>>
Obviously you need to adapt the rest of the code.
For example, when you need to add something to the dictionary, if it's the first time you need to create the List with that single document, next time you need to take the already created list and add documents there.

Android sort array

i have a string array consisting of a name and a score. I want to sort that array by score. Problem is, considering it's a string array, the scores are strings which results in 13,16,2,5,6 and not 2,5,6,13,16. I am using this code:
int spaceIndex;
String[][] scoreboard;
String[] playername;
String[] score;
int sbsize;
array1.add("Thomas" + ":" + 5);
array1.add("Blueb" + ":" + 6);
array1.add("James" + ":" + 16);
array1.add("Hleb" + ":" + 13);
array1.add("Sabbat" + ":" + 2);
sbsize = array1.size();
scoreboard = new String[sbsize][2];
playername = new String[sbsize];
score = new String[sbsize];
pos2 = new int[sbsize];
for (int i=0; i<array1.size(); i++)
{
spaceIndex = array1.get(i).indexOf(':');
scoreboard[i][0] = array1.get(i).substring(0, spaceIndex);
scoreboard[i][1] = array1.get(i).substring(spaceIndex+1, array1.get(i).length());
}
Arrays.sort(scoreboard, new Comparator<String[]>() {
#Override
public int compare(String[] entry1, String[] entry2) {
String time1 = entry1[1];
String time2 = entry2[1];
return time1.compareTo(time2);
}
});
What is the solution?
Cast them to int. As I recall, something like...
Arrays.sort(scoreboard, new Comparator<String[]>() {
#Override
public int compare(String[] entry1, String[] entry2) {
Integer time1 = Integer.valueOf(entry1[1]);
Integer time2 = Integer.valueOf(entry2[1]);
return time1.compareTo(time2);
}
});
Also you can make simple value object class for easier manipulations. Like...
class Player
{
public String name;
public int score;
}
And after that you can make
Player[] scoreboard = ...
Arrays.sort(scoreboard, new Comparator<Player>() {
#Override
public int compare(Player player1, Player player2) {
if(player1.score > player2.score) return 1;
else if(player1.score < player2.score) return -1;
else return 0;
}
});
Edit:
I recommend you to understand the basic OOP principles, this will help you a lot in the beginning.
Edit 2: Java 8 (with functional interface and a lambda):
Arrays.sort(scoreboard, (player1, player2) -> {
Integer time1 = Integer.valueOf(player1[1]);
Integer time2 = Integer.valueOf(player2[1]);
return time1.compareTo(time2);
});
This is the easy way of Sorting String Array:
Arrays.sort(mystringarray);
Use
java.util.Arrays.sort(yourArray, new Comparator<String>() {
#Override
public int compare(String object1, String object2) {
return Integer.valueOf(object1).compareTo(Integer.valueOf(object2));
}
});
Comparator will compare your strings as integers.
ArrayList<String> names= new ArrayList<String>();
names.add("sathish");
names.add("Ravi");
names.add("Praksh");
names.add("pavithara");
names.add("Duraga");
names.add("uma");
names.add("Upendar");
System.out.println("Before sorting");
System.out.println("Names : "+names);
Collections.sort(names, new Comparator<String>() {
#Override
public int compare(String lhs, String rhs) {
return lhs.compareToIgnoreCase(rhs);//Ascending order.
//return (lhs.compareToIgnoreCase(rhs)*(-1));//Descending order.
}
});
System.out.println("After sorting");
System.out.println("Names : "+names);
output:
Before sorting
Names : [sathish, Ravi, Praksh, pavithara, Duraga, uma, Upendar]
After sorting
Names : [Duraga, pavithara, Praksh, Ravi, sathish, uma, Upendar]
If possible use better data structure for your problem, use HashMap, with name to score mapping and , sort the hashmap with values.
If you want to go with arraylist as described by you, before sorting, convert them into integer and sort, then back to string.
You would probably be better off storing the names + results in objects, then storing those in an ArrayList. You can then sort very easily using a custom comparator, see the link for a simple example: http://www.javabeat.net/tips/20-sorting-custom-types-in-java.html
Score should be a class like
public class HighScore Comparable<HighScore>
{
private String name;
private int score;
public Score( String name, int score )
{
this.name = name;
this.score = score;
}//cons
//getters
public String getName() {
return name;
}//met
public int getScore() {
return score;
}//met
#Override
public int compareTo( HighScrore b )
{
int diffScore = score - b.score;
if( diffScore != 0)
return diffScore;
else
return name.compareTo( b.name );
}//met
public boolean equals( Object o )
{
if( !(o instanceof HighScore))
return false;
HighScore b = (HighScore) o;
return score == b.score && name.equals( b.name );
}//met
}//class
Then you can build score objects,
String[] stringParts[];
List<HighScore> listHighScore = new ArrayList<HighScore>();
for (int i=0; i<array1.size(); i++)
{
stringParts = array1.get(i).split(':');
listHighScore.add( new HighScore( stringParts[ 0 ], Integer.parseInt( stringParts[ 1 ])) );
}//for
put them in a List and sort them through
Collections.sort( list );
Regards,
Stéphane
You can use ArrayList instead of Array.
Please check this link

Categories

Resources