CompareTo() for finding the shortest/longest string in an array - java

I am trying to solve a exorcise that is supposed to learn me about the Comparable<T> interface. It tells me to find the shortest and longest string in a string array.
I think I am supposed to make my own compareTo() -method because the String.compareTo() method sorts alphabetically. But I can't get what my method should look like.
This is my code so far:
class ComparableTest implements Comparable<String> {
public static void main(String[] args) {
String arr[] = {"hei", "hvordan", "gaar", "det", "med", "deg", "a"};
String tempSto = arr[0]; //long string
String tempLit = arr[0]; //short string
for(String e : arr) {
if(e.compareTo(tempSto) > 0) {
tempSto = e;
}
if(e.compareTo(tempLit) < 0) {
tempLit = e;
}
}
System.out.println("Longest string is: " + tempSto);
System.out.println("Shortest string is: " + tempLit);
}
}

As you mentioned, you should implement your own Comparator, based on the String's length, not its alphabetical contents. E.g.:
public class StringLengthComparator extends Comparator<String> {
#Override
public int compare (String s1, String s2) {
return Integer.compare(s1.length(), s2.length();
}
}
Once you've done that, you can use it to find the shortest string in the array, or just reuse Collections#min(Collection, Comparator) to do the heavy lifting for you:
String shortest =
Collections.min(Arrays.asList(arr), new StringLengthComparator());

If your class implements the Comparable interface, i guess you have to override the compareTo method, and compare the length of the two String compared.

You can use a comparable to sort a list according to your own needs. This means in your case, that the highest and lowest number of characters in a string is relevant for the comparing. So, you subtract those value from each other in the comparing function. If you have for example house and dog, you have the two values 5 and 3. By subtracting the values from each other, you get the difference on how close those values are. So, just return the difference of the length of the two parameters in your ordering function. The first and last element in the list will then be the largest and smallest word (depending on which value you subtract from which).

In Java 8:
import java.util.Arrays;
public class Example {
public static void main(String[] args) {
String arr[] = {"hei", "hvordan", "gaar", "det", "med", "deg", "a"};
String minLengthStr = Arrays.stream(arr)
.min((str1, str2) -> Integer.compare(str1.length(), str2.length()))
.get();
String maxLengthStr = Arrays.stream(arr)
.max((str1, str2) -> Integer.compare(str1.length(), str2.length()))
.get();
System.out.println("Longest string is: " + maxLengthStr);
System.out.println("Shortest string is: " + minLengthStr);
}
}
Output:
Longest string is: hvordan
Shortest string is: a
Explanation:
The min method in the Stream class takes a Comparator as a parameter. As Comparator is a functional interface we can use a lambda expression as a short hand for implementing it.
So instead of writing:
Comparator<String> lengthComparator = new Comparator<String>() {
#Override
public int compare(String str1, String str2) {
return Integer.compare(str1.length(), str2.length());
}
};
You can write:
Comparator<String> lengthComparator = (str1, str2) -> Integer.compare(str1.length(), str2.length());
You can use this without streams too:
public class Example {
public static void main(String[] args) {
String arr[] = {"hei", "hvordan", "gaar", "det", "med", "deg", "a"};
String minLengthStr = arr[0], maxLengthStr = arr[0];
Comparator<String> lengthComparator = (str1, str2) -> Integer.compare(str1.length(), str2.length());
for(String str : arr) {
if(lengthComparator.compare(str, minLengthStr) == -1) {
minLengthStr = str;
} else if(lengthComparator.compare(str, maxLengthStr) == 1) {
maxLengthStr = str;
}
}
System.out.println("Longest string is: " + maxLengthStr);
System.out.println("Shortest string is: " + minLengthStr);
}
}

I hope,this will help
public class Test implements Comparator<String>{
public static void main(String[] args) {
// TODO Auto-generated method stub
//String arr[] = {"hei", "hvordan", "gaar", "det", "med", "deg", "a"};
Set<String> set = new TreeSet<String>(new Test());
set.add("hei");
set.add("hvordan");
set.add("gaar");
set.add("med");
set.add("deg");
set.add("a");
System.out.println(set);
}
#Override
public int compare(String o1, String o2) {
// TODO Auto-generated method stub
if(o1.length()>o2.length())
return 1;
else if(o1.length()<o2.length())
return -1;
else
return 0;
}

If you want to play with String, Comparable and compareTo here is an example.
ideone.com
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Test {
String arr[] = {"hei", "hvordan", "gaar", "det", "med", "deg", "a"};
/**
* Use composition.
*/
class MyComparableString implements Comparable<MyComparableString> {
String myString;
MyComparableString(String s) {
myString = s;
}
#Override
public int compareTo(MyComparableString other) {
// Compare the lengths of the strings in this and other.
Integer l1 = myString.length();
Integer l2 = other.myString.length();
return l1.compareTo(l2);
}
// String representation.
public String toString() {
return myString;
}
}
void go() {
// Convert the String array into a List (Collection) of MyComparableString.
List<MyComparableString> l = new ArrayList<>();
for (String s: arr) {
l.add(new MyComparableString(s));
}
// Print longest and shortest.
System.out.println("Shortest: " + Collections.min(l));
System.out.println("Longest: " + Collections.max(l));
}
public static void main(String[] args) {
new Test().go();
}
}

Related

Sort strings by index number of word (the number is given as an argument) in java

For example if I know for certain that I need sort by second word. I can create Comparator like this.
class SecondWordComparator implements Comparator<String>
{
#Override
public int compare(String s1, String s2)
{
String[] a1 = s1.split(" ");
String[] a2 = s2.split(" ");
return a1[1].compareTo(a2[1]);
}
}
Is it possible to give another index number of word to Comparator through an argument?
Not through an argument (parameter) to Comparator, no, but you can either:
Make it a field of your Comparator concrete class, or
If you implement Comparator as an anonymous class it can be a final variable or parameter the implementation closes over
#1 is fairly trivial. Here's an example of #2 (live copy):
import java.util.*;
class Example
{
public static void main (String[] args) throws java.lang.Exception
{
String[] strings = new String[] {
"one two three",
"uno due tre",
"uno dos tres",
"un deux trois"
};
sort(strings, 2); // 2 = third word
for (String s : strings) {
System.out.println(s);
}
}
private static void sort(String[] strings, final int index) {
Arrays.sort(strings, new Comparator<String>() {
#Override
public int compare(String s1, String s2)
{
String[] a1 = s1.split(" ");
String[] a2 = s2.split(" ");
String word1 = a1.length > index ? a1[index] : "";
String word2 = a2.length > index ? a2[index] : "";
return word1.compareTo(word2);
}
});
}
}
Since you are overriding a method from an interface
#Override
public int compare(String s1, String s2)
then adding a parameter in the method signature violates the declarede method in the interface....
so is not possible...
java is very flexible language so you can always implement anonymous instances of the comparator...

Sorting Array List containing strings and integers

If I have an ArrayList as the following:
["a 100", "b 32", "t 54", "u 1"] (numbers and letter are separated by space in each cell of the array list).
How can I sort it by numbers keeping each number with its corresponding letter?.
This looks like you are trying to implement object oriented programming using strings. Luckily, Java has already done this.
So, do something like this instead:
public class MyClass implements Comparable<MyClass> {
private final String aString; //could be char perhaps..
private final Integer anInteger;
public MyClass(final String aString, final Integer anInteger) {
this.aString = aString;
this.anInteger = anInteger;
}
public String getAString() { return aString; }
public Integer getAnInteger() { return anInteger; }
public String toString() { return anInteger + " " + aString }
//comparison by number
public int compareTo(final MyClass other) {
return anInteger.compareTo(other.anInteger);
}
}
Then, you use it like this:
final List<MyClass> myClasses = new ArrayList<>();
myClasses.add(new MyClass("a", 100));
myClasses.add(new MyClass("b", 32));
myClasses.add(new MyClass("t", 54));
myClasses.add(new MyClass("u", 1));
Collections.sort(myClasses);
You can simply use swapping method just like in regular arrays. The only difference is that we use set(index, "value") method to update a specific string at specified index.
public static void sort (ArrayList<String> arr){
int N = arr.size();
int E = N-1;
String temp;
boolean flag = true;
while(flag){
flag=false;
for(int a = 0 ; a < E ; a++){
if(Integer.parseInt(arr.get(a).substring(arr.get(a).indexOf(" ")+1)) >
Integer.parseInt(arr.get(a+1).substring(arr.get(a+1).indexOf(" ")+1))) {
temp=arr.get(a);
arr.set(a, arr.get(a+1));
arr.set(a+1, temp);
flag=true;
}
}
E--;
}}
The sorting algorithm is bubble sort. I have used it due to simplicity. You can use any other sorting algorithm if you want.
Then, you can call the sort() function in main method:
public static void main(String[] args) {
ArrayList<String> arr = new ArrayList<String>();
arr.add("a 98");
arr.add("a 23");
arr.add("c 11");
sort(arr);
}
Use a custom comparator to sort the list.
List<String> yourList = Arrays.asList("a 100", "b 32", "t 54", "u 1");
yourList.sort((entry1, entry2) -> {
int number1 = Integer.parseInt(entry1.split(" ")[1]);
int number2 = Integer.parseInt(entry2.split(" ")[1]);
return Integer.compare(number1, number2);
});
Regards
import static java.lang.Integer.*;
Just import static Integer methods and you'll get the most compact Comparator<String> for your purpose.
(a, b) -> compare(valueOf(a.split(" ")[1]), valueOf(b.split(" ")[1]));
Assuming the elements in the list are the same pattern:
then you can do
public static void main(String[] args) {
// ["a 100", "b 32", "t 54", "u 1"]
List<String> myList = new ArrayList<>();
myList.add("a 100");
myList.add("b 32");
myList.add("t 54");
myList.add("u 1");
System.out.println("List unsorted" + myList);
Collections.sort(myList, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
try {
int a1 = Integer.parseInt(o1.substring(2));
int a2 = Integer.parseInt(o2.substring(2));
return Integer.compare(a1,a2);
} catch (NumberFormatException ex) {
return 0;
}
}
});
System.out.println("List sorted" + myList);
}
In Java 8, Comparator has a handful of static and default methods that make it easy to create custom comparators. For instance, you could create one that splits each string and converts the second word to an integer.
list.sort(Comparator.comparingInt(
s -> Integer.parseInt(s.split(" ")[1])
));

recursively finding value of a String from a map

I have a hashmap containing Key and Value <String, String>.
i.e. mapValue:
mapValue.put("A","B-7");
mapValue.put("B","START+18");
mapValue.put("C","A+25");
Now I want to evaluate expression for 'C'. So for C, the expression would be
replaced by (((START+18)-7)+25).
So if anymethod, I will pass the string C, it should return string
"(((START+18)-7)+25)" and also I want to evaluate it as per the priority.
Thanks
generally logic of such function (assuming, you know possible operations and syntax is strict) may as follows:
public String eval(HashMap<String, String> mapValue, String variable) {
//get expression to be evaluated
String tmp = mapValue.get(variable);
// For each knwon operation
for (String op : OPERATIONS) {
// split expression in operators in Array
String[] vars = tmp.split("\\" + op);
// for each Element of splitted expr. Array
for (int i = 0; i < vars.length; i++) {
//Check if Element is a valid key in HashMap
if (mapValue.containsKey(vars[i])) {
//if it is replace element with result of iteration
vars[i] = eval(mapValue, vars[i]); // DO ITERATION
}
//if Element is not a valid key in has do nothing
}
//Join splitted string with proper operator
tmp = join(vars, op);
}
//return in parenthesis
return "(" + tmp + ")";
}
The result of 'eval(mapValue,"C")' would be:
(((START+18)-7)+25)
Some short join function may be implemented as follows:
public String join(String[] arr, String d) {
String result = arr[0];
int i = 1;
while (i < arr.length) {
result += d + arr[i];
i++;
}
return result;
}
All code provided above is more to illustrate logic, as some exception handling, better operations with string etc should be used.
Hope it helps
Cheers!
As mentioned in the comments I would not recommend recursion - it can lead to stackoverflow-Exceptions, if the recursion gets too deep.
Also I would recommend not to use String equations. Strings are slow to parse and can lead to unexpected results (as mentioned by #rtruszk "START" contains variable "A").
I created an example as my recommendation:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class X {
static interface Expression {
}
static class Combination implements Expression {
Expression[] values;
public Combination(Expression... values) {
this.values = values;
}
#Override
public String toString() {
return "?";
}
}
static class Reference implements Expression {
private String reference;
public Reference(String reference) {
this.reference = reference;
}
#Override
public String toString() {
return reference;
}
}
static class Number implements Expression {
private int value;
public Number(int value) {
this.value = value;
}
#Override
public String toString() {
return ""+value;
}
}
public static void main(String[] args) {
Map<String, Expression> mapValue = new HashMap<>();
mapValue.put("START", new Number(42));
String x = "C";
mapValue.put("A", new Combination( new Reference("B"), new Number(-7)));
mapValue.put("B", new Combination(new Reference("START"), new Number(+18)));
mapValue.put("C", new Combination( new Reference("A"), new Number(+25)));
int result = 0;
ArrayList<Expression> parts = new ArrayList<>();
parts.add(mapValue.get(x));
while (!parts.isEmpty()) {
debuggingOutput(x, result, parts);
Expression expression = parts.remove(0);
if (expression instanceof Combination)
parts.addAll(Arrays.asList(((Combination) expression).values));
else if (expression instanceof Reference)
parts.add(mapValue.get(((Reference) expression).reference));
else if (expression instanceof Number)
result += ((Number) expression).value;
}
System.out.println(result);
}
private static void debuggingOutput(String x, int result, ArrayList<Expression> parts) {
System.out.print(x);
System.out.print(" = ");
System.out.print(result);
for (Expression part : parts) {
System.out.print(" + ");
System.out.print(part);
}
System.out.println();
}
}

Need to sort strings by part of the string

Say I have an array of strings:
String[] array = {
"2183417234 somerandomtexthere",
"1234123656 somemorerandomtexthere",
"1093241066 andevenmore",
"1243981234 you get what i mean",
//etc
};
How would I sort this array using the long (it's a long) at the start of the string, so it'll end up looking like this:
String[] array = {
"1093241066 andevenmore",
"1234123656 somemorerandomtexthere",
"1243981234 you get what i mean",
"2183417234 somerandomtexthere",
//etc
};
I've tried everyting from making it an arraylist and using Collections#sort to creating my own comparator, to using a sorted map / tree map and I just can't figure it out.
Thanks.
Use this function:
static long comparedValue(String s) {
return Long.valueOf(s.substring(0, s.indexOf(' ')));
}
and then define a Comparator in terms of it:
public int compare(String left, String right) {
return comparedValue(left) - comparedValue(right);
}
Using Google Guava:
List<String> unsorted = Arrays.asList(array);
Function<String, Long> longFunction = new Function<String, Long>() {
#Override public Long apply(String input) {
return Long.valueOf(input.split(" ")[0]);
}
};
List<String> sorted = Ordering.natural().onResultOf(longFunction).immutableSortedCopy(unsorted);
Or if you don't wanna use a List (you should always prefer collections to arrays):
Arrays.sort(array, Ordering.natural().onResultOf(longFunction));
The input that you have shown works perfectly fine. But that's because all of them have the same number of digits.
public static void main(String[] args) {
String[] array = { "2183417234 somerandomtexthere",
"1234123656 somemorerandomtexthere", "1093241066 andevenmore",
"1243981234 you get what i mean", "999 little shorter"
// etc
};
List<String> list = Arrays.asList(array);
Collections.sort(list);
System.out.println(list);
}
Problems start to occur, when you use some shorter numbers - as 999 shown above...
output will be:
[1093241066 andevenmore, 1234123656 somemorerandomtexthere, 1243981234 you get what i mean, 2183417234 somerandomtexthere, 999 little shorter]
So, to make it working allways - you need your custom comparator, that will be able to split given Strings, and then take the number part out of them, and compare them. Using #Marko Topolik solution:
static long comparedValue(String s) {
return Long.valueOf(s.substring(0, s.indexOf(' ')));
}
public int compare(String left, String right) {
long result = comparedValue(left) - comparedValue(right);
boolean numberPartAreEqual = result == 0;
if (numberPartAreEqual) {
result = left.compareTo(right);
}
return (int) result;
}
A custom comparator should work fine:
public class LongPrefixComparator implements Comparator<String> {
#Override
public int compare(String s1, String s2) {
final long pref1 = getPrefixValue(s1);
final long pref2 = getPrefixValue(s2);
return s1 == s2 ? 0 : s1 < s2 ? -1 : 1;
}
private static long getPrefixValue(String stg) {
int len = stg.indexOf(' ');
if (len > 0) {
try {
return Long.parseLong(stg.substring(0, len));
catch (NumberFormatException ignored) {}
}
return 0L;
}
}

TreeSet Custom Comparator Algo .. String Comparision

From the input string provided:
{ "200,400,7,1", "100,0,1,1", "200,200,3,1", "0,400,11,1",
"407,308,5,1","100,600,9,1" } ,
I am adding the same in a TreeSet and want it to be sorted with the 3rd element order, so the expected output will be:
(100,0,1,1) (200,200,3,1) (407,308,5,1) (200,400,7,1) (100,600,9,1) (0,400,11,1)
But my actual output is:
(100,0,1,1)(0,400,11,1)(200,200,3,1)(407,308,5,1)(200,400,7,1)(100,600,9,1)
But since the string comparison of 11 is less than 9 but in terms of integer , 11>9 . My expected output is getting differed. Suggest me some idea to resolve the same.
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetComparator {
public static void main(String[] args) {
Comparator<String> comparator = new Comparator<String>() {
#Override
public int compare(String a, String b) {
String aStr = a;
String bStr = b;
String[] splitA = aStr.split(",");
String[] splitB = bStr.split(",");
return splitA[2].compareTo(splitB[2]);
}
};
String[] arr = { "200,400,7,1", "100,0,1,1", "200,200,3,1",
"0,400,11,1", "407,308,5,1", "100,600,9,1" };
TreeSet<String> ts = new TreeSet<String>(comparator);
for (String str : arr) {
ts.add(str);
}
for (String element : ts)
System.out.print(element + " ");
}
}
You're sorting in a lexicographical order ("123" comes before "20"), what you need to do is convert them to integers, and then compare them:
Not:
return splitA[2].compareTo(splitB[2]);
but:
return Integer.valueOf(splitA[2]).compareTo(Integer.valueOf(splitB[2]));
However, a somewhat cleaner way would be to create a custom object holding these 4 different values and then create a Comparator that compares the 3rd value of such an object:
The following:
public class Main {
public static void main (String[] args) {
Comparator<CustomObject> sortOn3rdValue = new Comparator<CustomObject>() {
#Override
public int compare(CustomObject o1, CustomObject o2) {
return o1.v3 < o2.v3 ? -1 : o1.v3 > o2.v3 ? 1 : 0;
}
};
Set<CustomObject> objects = new TreeSet<CustomObject>(sortOn3rdValue);
String[] arr = { "200,400,7,1", "100,0,1,1", "200,200,3,1", "0,400,11,1", "407,308,5,1", "100,600,9,1" };
for(String a : arr) {
objects.add(new CustomObject(a.split(",")));
}
for(CustomObject co : objects) {
System.out.println(co);
}
}
}
class CustomObject {
final int v1, v2, v3, v4;
CustomObject(String[] strValues) {
// assume strValues.lenght == 4
v1 = Integer.valueOf(strValues[0]);
v2 = Integer.valueOf(strValues[1]);
v3 = Integer.valueOf(strValues[2]);
v4 = Integer.valueOf(strValues[3]);
}
#Override
public String toString() {
return String.format("(%d,%d,%d,%d)", v1, v2, v3, v4);
}
}
would print:
(100,0,1,1)
(200,200,3,1)
(407,308,5,1)
(200,400,7,1)
(100,600,9,1)
(0,400,11,1)

Categories