Finding an enum item by its attribute - kotlin

I've got an enum in Kotlin:
enum class ParticleMode(val modeName: String) {
ABOVEHEAD("abovehead"),
BELOWHEAD("belowhead")
}
In my case, the user needs to be able to select items by typing a name. So, I need to be able to get ParticleMode.ABOVEHEAD by its modeName ("abovehead"), same for BELOWHEAD, etc. How can I get an enum item by its modeName value?

You can filter all values from you enum like this:
fun returnEnumFromString(string: String): Test {
return ParticleMode.values().first { it.modeName == string }
}

Related

Can I access enum property with string in kotlin?

I have an enum of regions like this:
enum class Regions(val location:String){
REGION_1("London"),
}
Is there a way to access the properties of region_1 with just a string like in the below function?
fun access(name:String){
return Regions.<modifed_name>.location
}
You can convert your string to enum using valueOf(value: string) and then use the location
fun access(name:String): String = Regions.valueOf(name.uppercase()).location
enum class Regions(val location: String) {
REGION_1("London"),
REGION_2("Berlin"),
REGION_3("Pairs")
}
fun access(name:String): String {
return Regions.values().firstOrNull() { it.name == name }?.location ?: "location not found"
}
println(access("REGION")) // Output: location not found
println(access("REGION_2")) // Output: Berlin

sort list of custom objects based on enum in kotlin

I have a list of custom object and an Enum class. I have to sort the list based on the enum properties. For example:
My list:
val list = listOf(Item(1,Severity.CRITICAL), Item(3, Severity.LOW), Item(2, Severity.SEVERE))
My enum class:
enum class Severity{
CRITICAL, SEVERE, MODERATE, LOW
}
My sorted list should be:
listOf(Item(1,Severity.CRITICAL), Item(2, Severity.SEVERE), Item(3, Severity.LOW))
Ordering is done as per the order in enum class. How can I achieve this?
Use sortedBy function. It will do the job very easily.
val ans = list.sortedBy { it.severity }
Just attach a value to each enum. This way you don't have to worry about the order in the enum class definition.
enum class Severity(val value: Int) {
SEVERE(1),
CRITICAL(0),
MODERATE(2),
LOW(3)
}
And then you could sort the list this:
list.sortedBy { it.serverity.value }
It's very simple.
Enum:
enum class Severity() {
CRITICAL,
SEVERE,
MODERATE,
LOW
}
List:
val list = listOf(Item(1,Severity.CRITICAL), Item(3, Severity.LOW), Item(2, Severity.SEVERE))
Kotlin ENUM classes has an inside index variable ordinal. It is set from 0 in the order of the ENUM you create.
So sort it by just like this:
list.sortedBy { it.severity.ordinal }
You can create an extension function from your enum as below:
private fun Severity.getOrder() = when(this) {
Severity.CRITICAL -> 1
Severity.SEVERE -> 2
...
}
and then, just:
.sortedBy { it.severity.getOrder() }
Doing this, you avoid depending on your enum order definition and let your "ordering" class to set that custom order.
Since enum is a comparable,an object of enum can be sorted by extracting the enum value
from the object, and use compareBy
val sortedList = list.sortedWith(compareBy { it.severity })
Edit:
If the ordering of the item is a 'natural' part of it, consider implements Comparable interface
So it will be like
data class Item(val index: Int, val severity: Severity) : Comparable<Item> {
override fun compareTo(other: Item): Int {
return this.severity.compareTo(other.severity)
}
}
val sortedList= list.sorted()

Does Kotlin allow you to assign custom value to enum?

This is throwing the error: Exception in thread "main" java.lang.IllegalArgumentException: No enum constant Color.red
enum class Color(val value: String = "") {
RED("red"),
YELLOW("yellow"),
BLUE("blue")
}
fun main() {
print(Color.valueOf("red"))
}
The above will only work if I change the print statement to:
print(Color.valueOf("RED"))
Is it possible to use a custom string to assign to an enum value using the valueOf?
As you discovered, the enum valueOf() method looks up by the name of the enum constant, not by any properties you add.
But you can easily add your own lookup method, using whatever criteria you want:
enum class Color(val hue: String) {
RED("red"),
YELLOW("yellow"),
BLUE("blue");
companion object {
fun forHue(hue: String) = values().find{ it.hue == hue }
}
}
A call to Color.forHue("red") returns the Color.RED instance as expected.
(This is probably the simplest approach, but not the most efficient; see answers such as this.)
No, but you can write your own method and get the value by iteration, when, or map.
Also, you cannot override valueOf.
You can implement your own valueOfwhich works case-insensitively:
enum class Color(val value: String = "") {
RED("red"),
YELLOW("yellow"),
BLUE("blue");
companion object {
//TODO: gimme a better name
fun customValueOf(val: String) = valueOf(val.toUpperCase())
}
}

How do I create an enum from an Int in Kotlin?

I have this enum:
enum class Types(val value: Int) {
FOO(1)
BAR(2)
FOO_BAR(3)
}
How do I create an instance of that enum using an Int?
I tried doing something like this:
val type = Types.valueOf(1)
And I get the error:
Integer literal does not conform to the expected type String
enum class Types(val value: Int) {
FOO(1),
BAR(2),
FOO_BAR(3);
companion object {
fun fromInt(value: Int) = Types.values().first { it.value == value }
}
}
You may want to add a safety check for the range and return null.
Enum#valueOf is based on name. Which means in order to use that, you'd need to use valueof("FOO"). The valueof method consequently takes a String, which explains the error. A String isn't an Int, and types matter. The reason I mentioned what it does too, is so you know this isn't the method you're looking for.
If you want to grab one based on an int value, you need to define your own function to do so. You can get the values in an enum using values(), which returns an Array<Types> in this case. You can use firstOrNull as a safe approach, or first if you prefer an exception over null.
So add a companion object (which are static relative to the enum, so you can call Types.getByValue(1234) (Types.COMPANION.getByValue(1234) from Java) over Types.FOO.getByValue(1234).
companion object {
private val VALUES = values()
fun getByValue(value: Int) = VALUES.firstOrNull { it.value == value }
}
values() returns a new Array every time it's called, which means you should cache it locally to avoid re-creating one every single time you call getByValue. If you call values() when the method is called, you risk re-creating it repeatedly (depending on how many times you actually call it though), which is a waste of memory.
Admittedly, and as discussed in the comments, this may be an insignificant optimization, depending on your use. This means you can also do:
companion object {
fun getByValue(value: Int) = values().firstOrNull { it.value == value }
}
if that's something you'd prefer for readability or some other reason.
The function could also be expanded and check based on multiple parameters, if that's something you want to do. These types of functions aren't limited to one argument.
If you are using integer value only to maintain order, which you need to access correct value, then you don't need any extra code. You can use build in value ordinal. Ordinal represents position of value in enum declaration.
Here is an example:
enum class Types {
FOO, //Types.FOO.ordinal == 0 also position == 0
BAR, //Types.BAR.ordinal == 1 also position == 1
FOO_BAR //Types.FOO_BAR.ordinal == 2 also position == 2
}
You can access ordinal value simply calling:
Types.FOO.ordinal
To get correct value of enum you can simply call:
Types.values()[0] //Returns FOO
Types.values()[1] //Returns BAR
Types.values()[2] //Returns FOO_BAR
Types.values() returns enum values in order accordingly to declaration.
Summary:
Types.values(Types.FOO.ordinal) == Types.FOO //This is true
If integer values don't match order (int_value != enum.ordinal) or you are using different type (string, float...), than you need to iterate and compare your custom values as it was already mentioned in this thread.
It really depends on what you actually want to do.
If you need a specific hardcoded enum value, then you can directly use Types.FOO
If you are receiving the value dynamically from somewhere else in your code, you should try to use the enum type directly in order not to have to perform this kind of conversions
If you are receiving the value from a webservice, there should be something in your deserialization tool to allow this kind of conversion (like Jackson's #JsonValue)
If you want to get the enum value based on one of its properties (like the value property here), then I'm afraid you'll have to implement your own conversion method, as #Zoe pointed out.
One way to implement this custom conversion is by adding a companion object with the conversion method:
enum class Types(val value: Int) {
FOO(1),
BAR(2),
FOO_BAR(3);
companion object {
private val types = values().associate { it.value to it }
fun findByValue(value: Int): Types? = types[value]
}
}
Companion objects in Kotlin are meant to contain members that belong to the class but that are not tied to any instance (like Java's static members).
Implementing the method there allows you to access your value by calling:
var bar = Types.findByValue(2) ?: error("No Types enum value found for 2")
Note that the returned value is nullable, to account for the possibility that no enum value corresponds to the parameter that was passed in. You can use the elvis operator ?: to handle that case with an error or a default value.
If you hate declaring for each enum type a companion object{ ... } to achieve EMotorcycleType.fromInt(...). Here's a solution for you.
EnumCaster object:
object EnumCaster {
inline fun <reified E : Enum<E>> fromInt(value: Int): E {
return enumValues<E>().first { it.toString().toInt() == value }
}
}
Enum example:
enum class EMotorcycleType(val value: Int){
Unknown(0),
Sport(1),
SportTouring(2),
Touring(3),
Naked(4),
Enduro(5),
SuperMoto(6),
Chopper(7),
CafeRacer(8),
.....
Count(9999);
override fun toString(): String = value.toString()
}
Usage example 1: Kotlin enum to jni and back
fun getType(): EMotorcycleType = EnumCaster.fromInt(nGetType())
private external fun nGetType(): Int
fun setType(type: EMotorcycleType) = nSetType(type.value)
private external fun nSetType(value: Int)
---- or ----
var type : EMotorcycleType
get() = EnumCaster.fromInt(nGetType())
set(value) = nSetType(value.value)
private external fun nGetType(): Int
private external fun nSetType(value: Int)
Usage example 2: Assign to val
val type = EnumCaster.fromInt<EMotorcycleType>(aValidTypeIntValue)
val typeTwo : EMotorcycleType = EnumCaster.fromInt(anotherValidTypeIntValue)
A naive way can be:
enum class Types(val value: Int) {
FOO(1),
BAR(2),
FOO_BAR(3);
companion object {
fun valueOf(value: Int) = Types.values().find { it.value == value }
}
}
Then you can use
var bar = Types.valueOf(2)
Protocol orientated way with type-safety
interface RawRepresentable<T> {
val rawValue: T
}
inline fun <reified E, T> valueOf(value: T): E? where E : Enum<E>, E: RawRepresentable<T> {
return enumValues<E>().firstOrNull { it.rawValue == value }
}
enum class Types(override val rawValue: Int): RawRepresentable<Int> {
FOO(1),
BAR(2),
FOO_BAR(3);
}
Usage
val type = valueOf<Type>(2) // BAR(2)
You can use it on non-integer type, too.
I would build the 'reverse' map ahead of time. Probably not a big improvement, but also not much code.
enum class Test(val value: Int) {
A(1),
B(2);
companion object {
val reverseValues: Map<Int, Test> = values().associate { it.value to it }
fun valueFrom(i: Int): Test = reverseValues[i]!!
}
}
Edit: map...toMap() changed to associate per #hotkey's suggestion.
try this...
companion object{
fun FromInt(v:Int):Type{
return Type::class.java.constructors[0].newInstance(v) as Type
}
}
This is for anyone looking for getting the enum from its ordinal or index integer.
enum class MyEnum { RED, GREEN, BLUE }
MyEnum.values()[1] // GREEN
Another solution and its variations:
inline fun <reified T : Enum<T>> enumFromIndex(i: Int) = enumValues<T>()[i]
enumFromIndex<MyEnum>(1) // GREEN
inline fun <reified T : Enum<T>> enumFromIndex(i: Int) = enumValues<T>().getOrNull(i)
enumFromIndex<MyEnum>(3) ?: MyEnum.RED // RED
inline fun <reified T : Enum<T>> enumFromIndex(i: Int, default: T) =
enumValues<T>().getOrElse(i) { default }
enumFromIndex(2, MyEnum.RED) // BLUE
It is an adapted version of another answer. Also, thanks to Miha_x64 for this answer.
Another option...
enum class Types(val code: Int) {
FOO(1),
BAR(2),
FOO_BAR(3);
companion object {
val map = values().associate { it.code to it }
// Get Type by code with check existing codes and default
fun getByCode(code: Int, typeDefault_param: Types = FOO): Types {
return map[code] ?: typeDefault_param
}
}
}
fun main() {
println("get 3: ${Types.getByCode(3)}")
println("get 10: ${Types.getByCode(10)}")
}
get 3: FOO_BAR
get 10: FOO

Is it possible to get a random element from an enum class in Kotlin?

Like the title says:
class Answers {
enum class Answer(text: String) {
YES("Yes."),
No("No."),
MAYBE("Maybe."),
AGAIN("Ask again.")
}
val answers = Answer.values()
val rand = Random()
fun genAnswer ():String {
val n = rand.nextInt(3)+1
// return Answer[answers[n]].text
}
}
I want to pick an enum element randomly and return its text property. However, it seems I can't use its value to retrieve the element.
You can get a random enum value by doing:
val randomAnswer = Answer.values().toList().shuffled().first().text
Keep in mind that it goes for convenience over performance.
Remember to expose the text property with val. For now, it's just a constructor param:
enum class Answer(val text: String)
Answer.values().random() will do the job for you in Kotlin.
When you write val/var within the constructor for a param, it declares a property inside the class. When you do not write it, it is simply a parameter passed to the primary constructor. So just add val to the constructor param text which will make it a property with a getter and above program will work..
import java.util.*
class Answers {
enum class Answer(val text: String) {
YES("Yes."),
No("No."),
MAYBE("Maybe."),
AGAIN("Ask again.")
}
val answers: Array<Answer> = Answer.values()
val rand = Random()
fun genAnswer ():String {
val n = rand.nextInt(3)+1
return answers[n].text
}
}
fun main(String: Array<String>) {
print(Answers().genAnswer())
}
I use this generic functions for any type of enum
/**
* return random enum value of enum class T
*/
inline fun <reified T : Enum<T>> randomEnum(): T {
val enumValues: Array<T> = enumValues()
return enumValues[(0 until enumValues.size).random()]
}
/**
* return random Int from start to end of range
*/
fun IntRange.random(): Int =
Random().nextInt((endInclusive + 1) - start) + start
I had a Enum SupportedLocale with values for languages
I did it in 2 steps
Get a list of values for enum:
val languages: Array<SupportedLocale> = SupportedLocale.values()
Now select random value using ordinate position
val randomLocale = languages[Random().nextInt(languages.size)]
An easy solution that I have used is
enum class OPERATOR(val symbol:String){MULTIPLY("*"),DIVIDE("/"),ADD("+"),SUBTRACT("-"),REMINDER("-")}
private fun getRandomOperator():String{
val limit=OPERATOR.values().size-1
return OPERATOR.values()[IntRange(0,limit).random()].symbol
}