How to create a sorted merged list from two diffrent ArrayList of Objects based on a common value field in Kotlin? - sorting

I have two ArrayLists of different Data classes as given below:
class Record{
var id: Long = 0
var RecordId: Int = 0
var Record: String? = null
var title: String? = null
var description: String? = null
var longDate: Long = 0
}
class Type{
var id: Long = 0
var typeId: Int = 0
var subTypeId: Int = 0
var typeString: String? = null
var longDate: Long = 0
}
var recordsList: ArrayList<Record>
var typesList: ArrayList<Type>
Now, I want a merged list of these two which will be sorted based on a common field in both the Objects i.e. longDate. I have tried .associate , sortedBy, sortedWith(compareBy<>) etc. but could not achieve the desired result.
Here, also there is one point to note is that while comparing the two lists it is possible that one on them may be empty.

This will produce a List<Any> with all items sorted by longDate:
(recordsList + typesList)
.sortedBy {
when (it) {
is Record -> it.longDate
is Type -> it.longDate
else -> error("")
}
}
Or you might consider creating an interface that has val longDate: Long that both of these classes implement. Then you wouldn't need the when expression, and your List would be of the type of the interface.

Something like this should work, but I personally think that it is quite the code smell. There is no guarantee that Record.longDate is truly the same type as Type.longDate (we know that it is, since we create the model, but the compiler would never know).
val result = (recordsList + typesList).sortedBy {
when(it){
is Record -> it.longDate
is Type -> it.longDate
else -> error("incompatible list element $it")
}
}
And it would work something like this: (I've removed some parameters from the models as they don't really count here)
fun main() {
val recordsList = listOf(Record().apply { longDate = 5 }, Record().apply { longDate = 3})
val typesList = listOf(Type().apply { longDate = 3 }, Type().apply { longDate = 2 })
val result = (recordsList + typesList).sortedBy {
when(it){
is Record -> it.longDate
is Type -> it.longDate
else -> error("incompatible list element $it")
}
}
result.forEach{
println(it.toString())
}
}
class Record{
var longDate: Long = 0
override fun toString(): String {
return "Record(longDate=$longDate)"
}
}
class Type{
var longDate: Long = 0
override fun toString(): String {
return "Type(longDate=$longDate)"
}
}
This will output:
Type(longDate=2)
Record(longDate=3)
Type(longDate=3)
Record(longDate=5)
Doing it in a more generic way, so that you can create a fun where you state which property to be used from each object type would most likely use reflection, which I'd avoid at all costs.
So I would definitely consider if one object can inherit the other, or create an interface, or anything else.
I'll end with 2 questions: why no constructors? why ArrayList and not list?

Related

Replacing for loops for searching list in kotlin

I am trying to convert my code as clean as possible using the Kotlin's built-in functions. I have done some part of the code using for loops. But I want to know the efficient built-in functions to be used for this application
I have two array lists accounts and cards.
My goal is to search a specific card with the help of its card-number, in the array list named cards.
Then I have to validate the pin. If the pin is correct, by getting that gift card's customerId I have to search the account in the array list named accounts. Then I have to update the balance of the account.
These are the class which I have used
class Account{
constructor( )
var id : String = generateAccountNumber()
var name: String? = null
set(name) = if (name != null) field = name.toUpperCase() else { field = "Unknown User"; println("invalid details\nAccount is not Created");}
var balance : Double = 0.0
set(balance) = if (balance >= 0) field = balance else { field = 0.0 }
constructor(id: String = generateAccountNumber(), name: String?,balance: Double) {
this.id = id
this.balance = balance
this.name = name
}
}
class GiftCard {
constructor( )
var cardNumber : String = generateCardNumber()
var pin: String? = null
set(pin) = if (pin != null) field = pin else { field = "Unknown User"; println("Please set the pin\nCard is not Created");}
var customerId : String = ""
set(customerId) = if (customerId != "") field = customerId else { field = "" }
var cardBalance : Double = 0.0
set(cardBalance) = if (cardBalance > 0) field = cardBalance else { field = 0.0; println("Card is created with zero balance\nPlease deposit") }
var status = Status.ACTIVE
constructor(cardNumber: String = generateCardNumber(),
pin: String,
customerId: String,
cardBalance: Double = 0.0,
status: Status = Status.ACTIVE){
this.cardNumber = cardNumber
this.pin = pin
this.customerId = customerId
this.cardBalance = cardBalance
this.status = status
}
}
This is the part of code, I have to be changed :
override fun closeCard(cardNumber: String, pin: String): Pair<Boolean, Boolean> {
for (giftcard in giftcards) {
if (giftcard.cardNumber == cardNumber) {
if (giftcard.pin == pin) {
giftcard.status = Status.CLOSED
for (account in accounts)
account.balance = account.balance + giftcard.cardBalance
giftcard.cardBalance = 0.0
return Pair(true,true)
}
\\invalid pin
return Pair(true,false)
}
}
\\card is not present
return Pair(false,false)
}
Both classes are not very idiomatic. The primary constructor of a Kotlin class is implicit and does not need to be defined, however, you explicitly define a constructor and thus you add another one that is empty.
// good
class C
// bad
class C {
constructor()
}
Going further, Kotlin has named arguments and default values, so make use of them.
class Account(
val id: String = generateAccountNumber(),
val name: String = "Unknown User",
val balance: Double = 0.0
)
Double is a very bad choice for basically anything due to its shortcomings, see for instance https://www.floating-point-gui.de/ Choosing Int, Long, heck even BigDecimal would be better. It also seems that you don’t want the balance to ever go beneath zero, in that case consider UInt and ULong.
Last but not least is the mutability of your class. This can make sense but it also might be dangerous. It is up to you to decide upon your needs and requirements.
enum class Status {
CLOSED
}
#ExperimentalUnsignedTypes
class Account(private var _balance: UInt) {
val balance get() = _balance
operator fun plusAssign(other: UInt) {
_balance += other
}
}
#ExperimentalUnsignedTypes
class GiftCard(
val number: String,
val pin: String,
private var _status: Status,
private var _balance: UInt
) {
val status get() = _status
val balance get() = _balance
fun close() {
_status = Status.CLOSED
_balance = 0u
}
}
#ExperimentalUnsignedTypes
class Main(val accounts: List<Account>, val giftCards: List<GiftCard>) {
fun closeCard(cardNumber: String, pin: String) =
giftCards.find { it.number == cardNumber }?.let {
(it.pin == pin).andAlso {
accounts.forEach { a -> a += it.balance }
it.close()
}
}
}
inline fun Boolean.andAlso(action: () -> Unit): Boolean {
if (this) action()
return this
}
We change the return type from Pair<Boolean, Boolean> to a more idiomatic Boolean? where Null means that we did not find anything (literally the true meaning of Null), false that the PIN did not match, and true that the gift card was closed. We are not creating a pair anymore and thus avoid the additional object allocation.
The Boolean.andAlso() is a handy extension function that I generally keep handy, it is like Any.also() from Kotlin’s STD but only executes the action if the Boolean is actually true.
There's probably a million different ways to do this, but here's one that at least has some language features I feel are worthy to share:
fun closeCard(cardNumber: String, pin: String): Pair<Boolean, Boolean> {
val giftCard = giftcards.find { it.cardNumber == cardNumber }
?: return Pair(false, false)
return if (giftCard.pin == pin) {
giftCard.status = Status.CLOSED
accounts.forEach {
it.balance += giftCard.cardBalance
}
Pair(true, true)
} else
Pair(true, false)
}
The first thing to notice if the Elvis operator - ?: - which evaluates the right side of the expression if the left side is null. In this case, if find returns null, which is equivalent to not finding a card number that matches the desired one, we'll immediately return Pair(false, false). This is the last step in your code.
From there one it's pretty straight forward. If the pins match, you loop through the accounts list with a forEach and close the card. If the pins don't match, then we'll go straight to the else branch. In kotlin, if can be used as an expression, therefore we can simply put the return statement before the if and let it return the result of the last expression on each branch.
PS: I won't say this is more efficient than your way. It's just one way that uses built-in functions - find and forEach - like you asked, as well as other language features.
PPS: I would highly recommend to try and find another way to update the lists without mutating the objects. I don't know your use cases, but this doesn't feel too thread-safe. I didn't post any solution for this, because it's outside the scope of this question.

Using the filter function in kotlin

so the past couple of hours, i have been trying to understand how the filter function works in kotlin and if it has any correlation with that of Java.
basically, i have a code that's written in java and i would love to have it transcribed to kotlin
private List<Order> getFilteredOrders(Courier courier) {
String[] glovoBoxKeywords = glovoBoxWords.toLowerCase().split(",");
List<Vehicle> allowedVehicles = Arrays.asList(MOTORCYCLE, ELECTRIC_SCOOTER);
return orders.stream()
.filter(order -> {
String description = order.getDescription().toLowerCase();
if (!courier.getBox()) {
return Arrays.stream(glovoBoxKeywords).noneMatch(description::contains);
}
return true;
})
.filter(order -> {
Location pickupLocation = order.getPickup();
Location deliveryLocation = order.getDelivery();
Double distance = calculateDistance(pickupLocation, deliveryLocation);
if (distance > longDeliveryDistance) {
return allowedVehicles.contains(courier.getVehicle());
}
return true;
})
.collect(Collectors.toList());
}
i tried this but i got at this, and was literally stuck :(
private fun findFilteredOrder(courier: Courier) : List<Order> {
val glovoBoxKeyWords = glovoBoxWords.toLowerCase().split(",")
val allowedVehicles = listOf(Vehicle.ELECTRIC_SCOOTER, Vehicle.MOTORCYCLE)
orderList.filter { order ->
val description = order.getDescription().toLowerCase()
if(!courier.getBox()) {
}
true
}.filter {
val pickupLocation = it.getPickup()
val deliveryLocation = it.getDelivery()
val distance = calculateDistance(deliveryLocation, pickupLocation)
if(distance > longDeliveryDistance) {
courier.getVehicle() in allowedVehicles
}
true
}
}
Please this is my first attempt and doing something with kotlin, so please go easy guys. thanks, also i'd be appreciative if anyone could help me with informative stuff as to how to understand these kotlin functions better. let, apply, associateBy... etc.. THANKS
The filter function in Kotlin Collections has the same principle as other frameworks/libraries, including Java Streams. Given a predicate (a function from the type of the collection to Boolean) it will return a new collection with the elements matching the predicate. You can find more information and examples of other functions and operators in the official documentation and here.
Your code was almost there, I translate the Java Stream operation to Kotlin List and rewrite the return statements to remove the redundant if
private fun findFilteredOrder(courier: Courier) : List<Order> {
val glovoBoxKeyWords = glovoBoxWords.toLowerCase().split(",")
val allowedVehicles = listOf(Vehicle.ELECTRIC_SCOOTER, Vehicle.MOTORCYCLE)
orderList.filter { order ->
val description = order.getDescription().toLowerCase()
courier.getBox() || glovoBoxKeywords.none { it in description }
}.filter { order ->
val pickupLocation = order.getPickup()
val deliveryLocation = order.getDelivery()
val distance = calculateDistance(deliveryLocation, pickupLocation)
distance <= longDeliveryDistance || courier.getVehicle() in allowedVehicles
}
}
I don't know why no one mentioned the use of labels: https://kotlinlang.org/docs/returns.html#break-and-continue-labels.
Since this question has a nice google ranking, I'll add what I was originally searching for.
The OP probably was aware that filter needs a predicate that returns a Boolean and that the filter will return a list with the items that pass the predicate (the items which the predicate returned true).
What he was not aware is that we can "emulate" Java returns through Kotlin labels:
private fun findFilteredOrder(courier: Courier) : List<Order> {
val glovoBoxKeyWords = glovoBoxWords.toLowerCase().split(",")
val allowedVehicles = listOf(Vehicle.ELECTRIC_SCOOTER, Vehicle.MOTORCYCLE)
orderList.filter shouldSkip#{ order ->
val description = order.getDescription().toLowerCase()
if (courier.getBox()) {
return#shouldSkip true
}
if (glovoBoxKeywords.none { it in description }) {
return#shouldSkip true
}
return#shouldSkip false
}.filter shouldSkip# { order ->
val pickupLocation = order.getPickup()
val deliveryLocation = order.getDelivery()
val distance = calculateDistance(deliveryLocation, pickupLocation)
if (distance <= longDeliveryDistance) {
return#shouldSkip true
}
if (courier.getVehicle() in allowedVehicles) {
return#shouldSkip true
}
return#shouldSkip false
}
}
Since Kotlin allows us to return in the last block line and the return keyword returns to the outer scope, it is pretty easy to:
filter {
startPutting >= someMagic && andComplex ||
verificationsThat.is { hardToUnderstand }.because {
weNeedToReturnHere
}
}
The labels allow us to be more verbose but also more clear.

Filtering multiple times on one dictionary

I currently run this code:
searchterm = "test"
results = resultsArray.filter { $0.description.contains (searchterm!) }
My question is how do I search in company_name or place or any other field in my model and add it to the results.
Do I need to use filters together and then append the results to a new variable instance of my model?
EDIT:
If "test" is in company_name, place and description. I want all three results returned. However, if "test" is only in place, I need only place to be returned.
EDIT2:
This is an example of my model return. Is this a dictionary or an array? I'm sorry I dont 100% percent know the difference. I know ' "this": is ' what a dictionary looks like, however because there were [] brackets around them, I thought that made it an array...
struct GraphData {
var description: String
var company_name: String
var places: String
init(description: String, company_name: String, places: String){
self.description = description
self.company_name = company_name
self.places = places
}
func toAnyObject() -> Any {
print("return")
return [
"description": description,
"company_name": company_name,
"places": places,
]
}
The easiest way to do this would be to create a custom contains method in your model which can you can use to match the search term against any property in the model:
class YourModel {
var company_name: String
var description: String
var place: String
// ...
func contains(_ searchTerm: String) -> Bool {
return self.company_name.contains(searchTerm)
|| self.description.contains(searchTerm)
|| self.place.contains(searchTerm)
}
}
You can then simply filter using your custom method:
let searchTerm = "test"
let results = resultsArray.filter { $0.contains(searchTerm) }
Is this resultsArray a dictionary or an array?
You can do something like this
let searchTerm = "test"
let filter = resultsArray.filter{ $0.company_name!.contains(searchTerm) || $0.place!.contains(searchTerm) }
Edit
class TestClass: NSObject {
var place: String?
var company_name: String?
func contain(searchTerm: String) -> [String] {
var result = [String]()
if let placeVal = place, placeVal.contains(searchTerm) {
result.append(placeVal)
}
if let companyVal = company_name, companyVal.contains(searchTerm) {
result.append(companyVal)
}
return result
}
}
let searchTerm = "test"
let filter = resultsArray.map { $0.contain(searchTerm: searchTerm) }

Dictionary does not have a member named filter - but it should, shouldn't it?

I'm trying to get code from github for auto-completion to work, but am stuck with an error on line 6 (data.filter) that Dictionary does not have a member named filter. But everything I read in the documentation suggests dictionaries should have a filter method. I've tried every possible combination of unwrapping, self, etc, but the compiler then registers these changes as the error.
Obviously something is going on that I do not understand - any guidance is appreciated.
var data = Dictionary<String, AnyObject>()
func applyFilterWithSearchQuery(filter : String) -> Dictionary<String, AnyObject>
{
var lower = (filter as NSString).lowercaseString
if (data.count > 0) {
var filteredData = data.filter ({
if let match : AnyObject = $0["DisplayText"]{
return (match as NSString).lowercaseString.hasPrefix((filter as NSString).lowercaseString)
}
else{
return false
}
})
}
return filteredData
}
Never heard of dictionary filters - where have you read about that? Arrays do have a filter method, but not dictionaries. What you can do is filter keys or values (accessible via the respective dictionary properties).
You can implement a custom filter on a dictionary with the following code:
var filtered = dict.keys.filter { $0.hasPrefix("t") }.map { (key: $0, value: dict[$0]) }
var newDict = [String : AnyObject]()
for element in filtered {
newDict[element.key] = element.value
}
which filter keys, then maps each key to a (key, value) tuple, then add each tuple to a new dictionary.
Note that to filter keys I used hasPrefix("t") - replace that with something more appropriate to your case
var data = Dictionary<String, AnyObject>()
func applyFilterWithSearchQuery(filter: String) -> [String : AnyObject] {
var lower = filter.lowercaseString
var filteredData = [String : AnyObject]()
if (!data.isEmpty) {
Swift.filter(data) {
(key, value) -> Bool in
if key == "DisplayText" {
filteredData[key] = (value as? String)?.lowercaseString.hasPrefix(lower)
}
return false
}
/* use map function works too
map(data) {
(key, value) -> Void in
if key == "DisplayText" {
filteredData[key] = (value as? String)?.lowercaseString.hasPrefix(lower)
}
}
*/
}
return filteredData
}

Linq - 'Saving' OrderBy operation (c#)

Assume I have generic list L of some type in c#. Then, using linq, call OrderBy() on it, passing in a lambda expression.
If I then re-assign the L, the previous order operation will obviously be lost.
Is there any way I can 'save' the lambda expression I used on the list before i reassigned it, and re-apply it?
Use a Func delegate to store your ordering then pass that to the OrderBy method:
Func<int, int> orderFunc = i => i; // func for ordering
var list = Enumerable.Range(1,10).OrderByDescending(i => i); // 10, 9 ... 1
var newList = list.OrderBy(orderFunc); // 1, 2 ... 10
As another example consider a Person class:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
Now you want to preserve a sort order that sorts by the Name property. In this case the Func operates on a Person type (T) and the TResult will be a string since Name is a string and is what you are sorting by.
Func<Person, string> nameOrder = p => p.Name;
var list = new List<Person>
{
new Person { Id = 1, Name = "ABC" },
new Person { Id = 2, Name = "DEF" },
new Person { Id = 3, Name = "GHI" },
};
// descending order by name
foreach (var p in list.OrderByDescending(nameOrder))
Console.WriteLine(p.Id + ":" + p.Name);
// 3:GHI
// 2:DEF
// 1:ABC
// re-assinging the list
list = new List<Person>
{
new Person { Id = 23, Name = "Foo" },
new Person { Id = 14, Name = "Buzz" },
new Person { Id = 50, Name = "Bar" },
};
// reusing the order function (ascending by name in this case)
foreach (var p in list.OrderBy(nameOrder))
Console.WriteLine(p.Id + ":" + p.Name);
// 50:Bar
// 14:Buzz
// 23:Foo
EDIT: be sure to add ToList() after the OrderBy calls if you need a List<T> since the LINQ methods will return an IEnumerable<T>.
Calling ToList() or ToArray() on your IEnumerable<T> will cause it to be immediately evaluated. You can then assign the resulting list or array to "save" your ordered list.

Resources