Kotlin. ArrayList, how to move element to first position - algorithm

I have a list of Lessons. Here is my Lessons class:
data class Lessons(
val id: Long,
val name: String,
val time: Long,
val key: String
)
I need to move the element to the beginning of the list, whose key field has a value "priority".
Here is my code:
val priorityLesson = lessons.find { it.key == "priority" }
if (priorityLesson != null) {
lessons.remove(priorityLesson)
lessons.add(0, priorityLesson)
}
Everything is working but I do not like this solution, perhaps there is a more efficient way to perform this algorithm. In addition, it comes to me to convert the list to mutable, and I would like to leave it immutable.
Please help me.

One way is to call partition() to split the list into a list of priority lesson(s), and a list of non-priority lessons; you can then rejoin them:
val sorted = lessons.partition{ it.key == "priority" }
.let{ it.first + it.second }
As well as handling the case of exactly one priority lesson, that will cope if there are none or several. And it preserves the order of priority lessons, and the order of non-priority lessons.
(That will take a little more memory than modifying the list in-place; but it scales the same — both are 𝒪(n). It's also easier to understand and harder to get wrong!)

First, I would call your class Lesson rather than Lessons as it represents a single lesson. Your choice of the variable name lessons is good for your list of lessons.
You can use a mutable list and move the item to the top:
val priorityLessonIndex = lessons.indexOf { it.key == "priority" }
if (priorityLessonIndex != -1)
lessons[0] = lessons[priorityLessonIndex]
.also { lessons[priorityLessonIndex] = lessons[0] }
Or you can use an immutable list:
val priorityLesson = lessons.firstOrNull { it.key == "priority" }
val newList =
if (priorityLesson != null)
listOf(priorityLesson) + (lessons - priorityLesson)
else
lessons
A possibly more efficient way, which avoids creation of intermediate lists:
val newList = buildList(lessons.size) {
lessons.filterTo(this) { it.key == "priority" }
lessons.filterTo(this) { it.key != "priority" }
}

Related

How to sort an array of object in kotlin with custom order?

I have an array of object User(val name: String, val role: String). Role can be: leader, student, teacher, parent. And should be sorted in that order. I've read some article about Comparator but haven't figure out how they work. Can you please add some explanation too?
You can instantiate a HashMap for indicating roles priority:
private val roles: HashMap<String, Int> = hashMapOf(
"Leader" to 0,
"Student" to 1,
"Teacher" to 2,
"Parent" to 3
)
fun sortUsers(users: ArrayList<User>): ArrayList<User> {
val comparator = Comparator { o1: User, o2: User ->
return#Comparator roles[o1.role]!! - roles[o2.role]!!
}
val copy = arrayListOf<User>().apply { addAll(users) }
copy.sortWith(comparator)
return copy
}
In the comparator, if the result of the subtraction is negative, it will sort it in the order [o1, o2]. Otherwise, [o2, o1]. So in this case, it will sort your list in an ascending manner (since we have the highest priority as 0 - Leader as indicated in the HashMap).
For example, if we have o1 as Teacher and o2 as Leader,
the result of the comparator will be: 2 - 0 = 2 which is a positive integer. Hence, it is sorted as [o2 (Leader), o1 (Teacher)].
Reversing the roles however yields the opposite result: 0 - 2 = -2 and hence it will be ordered [o1 (Teacher), o2 (Leader)].
You can verify this result with:
fun main(args: Array<String>) {
val users = arrayListOf(
User("john", "Student"),
User("tim", "Teacher"),
User("nice", "Student"),
User("hey", "Leader"),
User("you", "Parent")
)
println(sortUsers(users))
}
Which prints out: [User(name=hey, role=Leader), User(name=john, role=Student), User(name=nice, role=Student), User(name=tim, role=Teacher), User(name=you, role=Parent)]
I believe the simplest way to solve your problem is to use enum classes, since they already implement the Comparable interface. You can't use String directly since it would use the String compareTo method, which sorts them alphabetically.
Essentially you would have to do something like this:
enum class Role { LEADER, STUDENT, TEACHER, PARENT }
data class User(val name: String, val role: Role)
fun main(args: Array<String>) {
val trumpino = User("trumpino", Role.STUDENT)
val obamino = User("barack-obamino", Role.PARENT)
val bushino = User("george-bushino", Role.TEACHER)
val kennedino = User("kennedino", Role.LEADER)
val mlist = listOf<User>(trumpino, obamino, bushino, kennedino)
val result = mlist.sortedBy { it.role }
println(result)
}
But that would require you to change other pieces of your code if you're already using strings. You could possibly add an extension method to parse the string into an enum or use the valueOf method which is explained in this other answer.
The comparator interface is meant to be used with the functional constructs from the kotlin library, in constrast with the comparable interface which is meant to represent the inherent ordering from your class (if it has one). If you need to order your data in a way that's different from its normal ordering (which would be defined by the comparable interface), you use comparator with some ordering method like sortedWith.
With comparator you could do something like, which specify a complex ordering in a simple manner:
mlist.sortedWith(compareByDescending { it.role }.thenBy { it.name })
In contrast to the comparable interface:
class User(val name: String, val role: Role) : Comparable {
override operator fun compareTo(other: User) : Int {
return when {
other.role > this.role -> 1
other.role < this.role -> -1
other.name > this.name -> 1
other.name < this.name -> -1
else -> 0
}
}
}
They both act in the same way in the sense that the value they return is meant to represent the ordering of between this and other, as explained by the kotlin documentation of compareTo:
Compares this object with the specified object for order. Returns zero if this object is equal to the specified other object, a negative number if it's less than other, or a positive number if it's greater than other.
But since comparator is a functional construct, it has a diferent interface from its OOP counterpart as it's meant to be used with lambda expressions.
abstract fun compare(a: T, b: T): Int
But it acts in the same way, as explained in the java documentation for Comparator
Returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
its so simple if you use kotlin, you can use sort method. for example
you have list like
val listOfUser = listOf<User>(
User("raka", "teacher"),
User("adi", "student"),
User("steve", "student"),
User("mark", "student"),
User("jack", "operator")
)
if you want to sort, you just access your variable and use sort method. like this
listOfUser.sortedBy {it.role} // if you want to sort ASCENDING by roles
listOfUser.sortedByDescending { it.role } // if you want to sort DESC by roles
Let your array be
val listOfUser = listOf<User>(
User("Ben", "Teacher"),
User("Bheem", "Student"),
User("Steve", "Student"),
User("Fora", "Student"),
User("Jacker", "Operator")
)
To sort this array you can use sortedWith method and a comparator of your object whose return type is also list is as shown below
val sortedUsersList = listOfUser.sortedWith(compareBy { it.role })
If you use sortBy or sortByDescending methods their return will be Unit but not list.

add data to an arrayList, when If condition is satisfied in nested for loop in Kotlin

Check the code below:
getCommonsArrayList(listA:ArrayList< User >, listB:ArrayList<User>):ArrayList<User>{
var listCommon = ArrayList<User>()
for (i in listA.indices) {
for (j in listB.indices) {
if (listA[i].id.equals(listB[j].id)) { //if id of the user matches
listCommon.put(listA[i]) //add to a new list
}
}
}
return listCommon // return the new list with common entries
}
The above method iterates list a & b and check whether the id's are matching, if they are then the User object is stored to a new list and at the end of the program, it returns the common list.
This thing works good. And I hope nested for followed by if condition is the way in which we can compare two lists.
The problem with this is if listA has repeated entries, then the listCommon will also have repeated entries as ArrayList supports duplicacy of entries.
So what I did to make commonList unique is I introduced a HashMap object as shown below:
getCommonsArrayList(listA:ArrayList< User >, listB:ArrayList<User>):ArrayList<User>{
var listCommon = ArrayList<User>()
var arrResponseMap = HashMap<String,User>()
for (i in listA.indices) {
for (j in listB.indices) {
if (listA[i].id.equals(listB[j].id)) { //if id of the user matches
arrResponseMap.put(listA[i].id,listA[i]) // add id to map so there would be no duplicacy
}
}
}
arrResponseMap.forEach {
listCommon.add(it.value) //iterate the map and add all values
}
return listCommon // return the new list with common entries
}
This will give the new arrayList of userObject with common Id's. But this has an increased complexity than the above code.
If the size of the listA and listB increases to 1000 then this execution will take heavy time.
Can someone guide me if there is some better way to solve this.
You can simply use distinctBy to get only unique values from list.
Official Doc:
Returns a sequence containing only elements from the given sequence
having distinct keys returned by the given selector function.
The elements in the resulting sequence are in the same order as they
were in the source sequence.
Here is an example:
val model1 = UserModel()
model1.userId = 1
val model2 = UserModel()
model1.userId = 2
val model3 = UserModel()
model1.userId = 1
val model4 = UserModel()
model1.userId = 2
val commonList = listOf(model1, model2, model3, model4)
// get unique list based on userID, use any field to base your distinction
val uniqueList = commonList
.distinctBy { it.userId }
.toList()
assert(uniqueList.count() == 2)
assert(commonList.count() == 4)
Add the both the list and use distinctBy like this
data class DevelopersDetail(val domain: String, val role: String)
val d1 = DevelopersDetail("a", "1")
val d2 = DevelopersDetail("b", "1")
val d3 = DevelopersDetail("c", "1")
val d4 = DevelopersDetail("c", "1")
val d5 = DevelopersDetail("d", "1")
var listA = listOf(d1, d2, d3, d4)
var listb = listOf(d1, d2, d3, d4)
var data = listA + listb
var list= data
.distinctBy { it.domain }
.toList()
println("list $list")
//output-list [DevelopersDetail(domain=a, role=1), DevelopersDetail(domain=b, role=1), DevelopersDetail(domain=c, role=1)]

What's a concise way of sorting and grouping a set in Kotlin?

From a set of Comparable objects I would like to get a sorted list of sets of objects, sorted by their natural order.
This is my solution:
val set: Set<Comparable<A>> = makeThings()
val sorted = set.sorted()
val sortedAndGrouped = mutableListOf(mutableSetOf(sorted.first()))
for(element in sorted.drop(1)) {
val randomElementOfLastGroup = sortedAndGrouped.last().first()
val doesNotBelongToLastGroup = randomElementOfLastGroup.compareTo(element) != 0
if(doesNotBelongToLastGroup)
sortedAndGrouped.add(mutableSetOf())
sortedAndGrouped.last().add(element)
}
I would like to know if there is a more concise alternative in Kotlin.
If not, is there one using Java 8 features or a library like Guava?
I would try something like this:
fun <A : Comparable<A>> alternativeSortAndGroup(set: Set<A>): List<Set<A>> =
set.sorted().groupBy { it }.values.map { it.toSet() }

Search for a list of words in a paragraph

I have a paragraph written in English.
I have a list of words.
I want to check if the paragraph contains any one word
What is the best algorithm to do so:
Presently, I have the following but it seems very naive:
private boolean findMatch(List<String> list, String param, ArrayList<String> skipChars) {
boolean matchResult = false;
for (String s : list) {
if(skipChars == null || !skipChars.contains(s)){
if (param.indexOf(s) != -1) {
matchResult = true;
break;
}
}
}
return matchResult;
}
}
split the paragraph to wrods, and store them in a hash table
now for each word in your list search for it in the hash.
for real life applications this will probably do.
--EDIT--
if you cannot split the paragraph into words, and you need to tell if only one word is in the paragraph I suggest constructing a trie from your list of words, and then going over the paragraph and checking the trie for matches as you go.
In c# i usually use linq to entities for quering list and get result.
this is my code:
private bool findMatch(List<String> list, String param, List<String> skipChars)
{
if (skipChars == null)
skipChars = new List<string>();
var c = (from l in list.Except(skipChars)
where param.IndexOf(l) != -1
select l).Count();
return c != 0;
}

Multidimensional data lookup

I have a collection of tuples of N values. A value may be a wildcard (matches any value), or a concrete value. What would be the best way to lookup all tuples in the collection matching a specific tuple without scanning the entire collection and testing items one by one?
E.g. 1.2.3 matches 1.*.3 and *.*.3, but not 1.2.4 or *.2.4.
What data structure am I looking for here?
I'd use a trie to implement this. Here's how I would construct the trie:
The data structure would look like:
Trie{
Integer value
Map<Integer, Trie> tries
}
To insert:
insert(tuple, trie){
curTrie = trie
foreach( number in tuple){
nextTrie = curTrie.getTrie(number)
//add the number to the trie if it isn't in there
if(nextTrie == null){
newTrie = new Trie(number)
curTrie.setTrie(number, newTrie)
}
curTrie = curTrie.getTrie(number)
}
}
To get all the tuples:
getTuples(tuple, trie){
if(head(tuple) == "*"){
allTuples = {}
forEach(subTrie in trie){
allTuples.union(getTuples(restOf(tuple), subTrie))
forEach(partialTuple in allTuples){
partialTuple = head(tuple)+partialTuple
}
}
return allTuples
}
if(tuple == null)
return {trie.value}
if(trie.getTrie(head(tuple)) == null)
raise error because tuple does not exist
allTuples = {}
allTuples.union(getTuples(restOf(tuple), trie.getTrie(head(tuple))
forEach(partialTuple in allTuples){
partialTuple = head(tuple)+partialTuple
}
return allTuples
}

Resources