Sorting a 2 dimensional array of objects in Kotlin - arrays

I have a static 2 dimensional array of objects in a Kotlin project:
class Tables {
companion object{
lateinit var finalTable: Array<Array<Any?>?>
}
}
It is a little clearer in Java:
public class Tables {
public static Object[][] finalTable;
}
The third element in one row of objects in the table, is a string boxed as an object. In other words: finalTable[*][2] is a string describing the item. When I add an item to the array in Kotlin, I want to sort the entire array in alphabetical order of the description.
In Java this is easy:
Arrays.sort(Tables.finalTable, Comparator.comparing(o -> (String) o[2]));
When I try to use Android Studio to translate the Java code into Kotlin, it produces the following:
Arrays.sort( Tables.finalTable, Comparator.comparing( Function { o: Array<Any?>? -> o[2] as String }) )
This does not work, you have change the String cast as follows:
Arrays.sort( Tables.finalTable, Comparator.comparing( Function { o: Array<Any?>? -> o[2].toString() }) )
This version will compile and run, but it totally messes up the sorting of the table, so it does not work. I have tried variations on this theme, without any success. To get my project to work, I had to create a Java class in the Kotlin project with the functional Java code listed above:
public class ArraySort {
public void sortArray(){
Arrays.sort(Tables.finalTable, Comparator.comparing(o -> (String) o[2]));
}
}
This sorts the table like a charm, but I would prefer to keep my project "pure Kotlin". Can anyone suggest a pure Kotlin method to sort such an array? Thanks!

Unless I'm missing something, you can just do this:
Tables.finalTable.sortBy { it[2] as String }
which sorts your array in place. sortedBy will produce a new copy of the original if that's what you want instead, and might be why the comment suggestions weren't working for you.
But this whole unstructured array situation isn't ideal, the solution is brittle because it would be easy to put the wrong type in that position for a row, or have a row without enough elements, etc. Creating a data structure (e.g. a data class) would allow you to have named parameters you can refer to (making the whole thing safer and more readable) and give you type checking too

Related

kotlin add elements in array as sections

Can anyone teach me how to initiate/add items in array as section?
something like - array[["john"],["daniel"],["jane"]]
tried a few like var d = ArrayList<CustomClass>(arrayListOf<CustomClass>())
that does not work.
for example, if i add "david" i want it look like array[["john"],["daniel", "david"],[“keith"]] by finding the index of array contains letter "d"
how can i display them on a custom base adapter / listview? currently using a viewHolder
val viewHolder = ViewHolder(row.name) to display.
Thanks!
If I am right, you're trying to create an ArrayList that contains other ArrayLists. So writing:
var d = ArrayList<CustomClass>(arrayListOf<CustomClass>())
Won't work because the type of the main ArrayList is not String, but ArrayList. So you need to write:
var names = ArrayList<ArrayList<String>>()
names.add(arrayListOf("jane", "john))
Regarding the second question, check out this course that has a section about RecyclerView: https://classroom.udacity.com/courses/ud9012 If you are a beginner, I strongly encourage you to follow the whole tutorial.

kotlin "contains" doesn't work as expected

I am working with a method which filters the preferences which match with the ids, I am using the contains method, but even though the values are the same, the contains is showing a false in each iteration. The method looks like:
private fun filterPreferencesByIds(context: MyPodCastPresenterContext): List<FanPreferences> {
return context.preferences?.filter {
context.ids.contains(it.id)
}
}
The values of the arrays are:
for the context.ids:
"B52594F5-80A4-4B18-B5E2-8F7B12E92958" and "3998EDE7-F84B-4F02-8E15-65F535080100"
And for the context.preferences:
But even though, when the first and the final ids have the same id value as the context.ids, the contains is false in the debug. I think it could be related with the types in the context.ids rows Json$JsonTextNode. Because when I did the same with numeric values hardcoded the compare is successful.
Any ideas?
Thanks!
If the type of FanPreferences.id is String and the type of context.ids list element is JsonTextNode, you won't find an element equal to the given id string, because it's not of String type.
Try mapping your context ids to the list of strings before filtering:
val ids = context.ids.map { it.toString() }.toSet()
return context.preferences?.filter {
ids.contains(it.id)
}
Note that calling toString() on JsonTextNode might be not the best way to get the string data from it. It's better to consult the API documentation of that class to find it out.

How to find which element failed comparison between arrays in Kotlin?

I'm writing automated tests for one site. There's a page with all items added to the cart. Maximum items is 58. Instead of verification of each element one by one I decided to create 2 arrays filled with strings: 1 with correct names : String and 1 with names : String I got from the site. Then I compare those 2 arrays with contentEquals.
If that comparison fails, how do I know which element exactly caused comparison fail?
Short simple of what I have now:
#Test
fun verifyNamesOfAddedItems () {
val getAllElementsNames = arrayOf(materials.text, element2.text,
element3.text...)
val correctElementsNames = arrayOf("name1", "name2", "name3"...)
val areArraysEqual = getAllElementsNames contentEquals correctElementsNames
if (!areArraysEqual) {
assert(false)
} else {
assert(true)
}
}
This test fails if 2 arrays are not the same but it doesn't show me the details, so is there a way to see more details of fail, e.g. element that failed comparison?
Thanks.
I recommend using a matcher library like Hamcrest or AssertJ in tests. They provide much better error messages for cases like this. In this case with Hamcrest it would be:
import org.hamcrest.Matchers.*
assertThat(getAllElementsNames, contains(*correctElementsNames))
// or just
assertThat(getAllElementsNames, contains("name1", "name2", "name3", ...))
There are also matcher libraries made specifically for Kotlin: https://github.com/kotlintest/kotlintest, https://yobriefca.se/expect.kt/, https://github.com/winterbe/expekt, https://github.com/MarkusAmshove/Kluent, probably more. Tests using them should be even more readable, but I haven't tried any of them. Look at their documentation and examples and pick the one you like.
You need to find the intersection between the two collections. Intersection will be the common elements. After than removing the intersection collection from the collection you want to perform the test will give you the complementary elements.
val intersection = getAllElementsNames.intersect(correctElementsNames)
getAllElementsNames.removeAll(intersection)

How to use Collections.binarySearch() in a CodenameOne project

I am used to being able to perform a binary search of a sorted list of, say, Strings or Integers, with code along the lines of:
Vector<String> vstr = new Vector<String>();
// etc...
int index = Collections.binarySearch (vstr, "abcd");
I'm not clear on how codenameone handles standard java methods and classes, but it looks like this could be fixed easily if classes like Integer and String (or the codenameone versions of these) implemented the Comparable interface.
Edit: I now see that code along the lines of the following will do the job.
int index = Collections.binarySearch(vstr, "abcd", new Comparator<String>() {
#Override
public int compare(String object1, String object2) {
return object1.compareTo(object2);
}
});
Adding the Comparable interface (to the various primitive "wrappers") would also would also make it easier to use Collections.sort (another very useful method :-))
You can also sort with a comparator but I agree, this is one of the important enhancements we need to provide in the native VM's on the various platforms personally this is my biggest peeve in our current VM.
Can you file an RFE on that and mention it as a comment in the Number issue?
If we are doing that change might as well do both.

How do you link two arrays?

I'm in a basic programming class, and everything is done in pseudo code.
My question is this: How do you link two arrays?
I have a single-dimensional array that lists students names, and I have a two-dimensional array that lists the top eight scores of each student...this is all fine and dandy, but now I need to sort the arrays by the students name. I'm poked around online and read through the books chapter twice, it only briefly mentions linking two arrays but shows no examples.
If it's any help, we are using bubble-sorting, and that is what I am fairly familiar with...I can sort the names, that's the easy part, but I don't know how to sort the grades so they do not go out of order.
Thanks for the input!
Sidenote: I got it figured out! I ended up doing how Greg Hewgill had mentioned. As I put in my comment to his suggestion, I started randomly throwing in lines of code until that idea hit me...it doesn't look pretty (one module swapped the names, another to swap the grades, and a third even then to swap the individual students grades earlier on in a multidimensional array), but it indeed seemed to work...no way to test it in a language as I have no compiler nor have I enough knowledge to make the pseudo code into actual code if I were to download one, but it sounds really good on the paper I typed it out on!
As I also mentioned in the note, I do thank everyone for their speedy and helpful insight, I actually didn't even think I'd get a reply tonight, thank you everyone again for all your help!
Jeffrey
Define a simple Student class like this:
public class Student : IComparable<Student>
{
public string Name { get; set; }
public int[] Scores { get; set; }
#region IComparable<Student> Members
public int CompareTo(Student other)
{
// Assume Name cannot be null
return this.Name.CompareTo(other.Name);
}
#endregion
}
then even simpler
var students = new[] {
new Student(){ Name = "B", Scores = new [] { 1,2,3 } },
new Student(){ Name = "C", Scores = new [] { 3,4,5 } },
new Student(){ Name = "A", Scores = new [] { 5,6,7 } }
};
Array.Sort(students);
will do the work for you.
What you may want to do is the following: As you're sorting the names and you have to swap two positions, do the same swap in the array of scores. That way, all changes that you make to the names array will be reflected in the scores array. When you're done, the scores will be in the same sorted order as the names are.
There are more effective ways of doing this with different data structures, as other comments will show.
Your premise is wrong. You shouldn't have two array in the first place.
You should have one array of objects, each of which holds a student's name and his scores:
public class Record
{
public string Student;
public int[] Scores;
}
Two approaches: first, when sorting the names, each time you exchange two names, exchange the rows (or columns or whatever you want to call them) of scores in the same positions. At the end, the scores should still be in sync with the names.
Second, instead of sorting the names, create a third array that will contain the indexes into either of the other two arrays, initially 0 through n-1, but then sorted, comparing name[a] and name[b], instead of sorting the names array itself.

Resources