0

I need to obtain some string that match with another. For example, when my string is “Ne” I need to obtain “Neon”. I made this, but I think that this isn’t the best way. I think that is better use sealed class. Is there some way to match a string with an element from a sealed class?

fun getName(symbol: String): String{
        return when(symbol){
            "Ne" -> {"Neon"}
            "Ar" -> {"Argon"}
            "Kr" -> {"Krypton"}
            "Xe" -> {"Xenon"}
            "Rn" -> {"Radon"}
            "Br" -> {"Bromine"}
            else -> {""}
        }
    }

thanks!

Electrocode
  • 127
  • 1
  • 8
  • I don't think there is . however i see the values u have are from periodic table which is limited and constant. So u can have a Map or List of pre-build data which can be easy to access .. – ADM Aug 21 '22 at 04:52
  • It's hard to answer without more context. I can recommend avoiding putting data intended for the UI in hardcoded strings, and setting up i18n https://developer.android.com/guide/topics/resources/localization. Have you tried this? Even if it's not for the UI, storing such mappings in a resource file is usually more suitable. – aSemy Aug 21 '22 at 06:02

3 Answers3

3

A sealed class probably won't help you much here, but an Enum could, especially if you later want to do more than just get the full name (e.g. get the atomic weight, localized string resource ID, or something too). It also provides you with a strong type, so you can define a function that takes an Element enum instead of just a string.

enum class Element(val full_name: String, val num: Int) {
    Ne("Neon",    10),
    W( "Tungsten",74),
    Fe("Iron",    26),
    MISSING("", -1);
    
    companion object {
        // or just use Element.valueOf(), but this lets you make
        // a MISSING entry to return instead if you want
        fun fromSymbol(sym: String): Element {
            return try {
                valueOf(sym)
            } catch(e: IllegalArgumentException) {
                MISSING;
            }
        }

        fun fromNumber(n: Int): Element {
             return values().firstOrNull { it.num == n } ?: MISSING
        }
    }
}

This is more extensible than the when approach because you can add extra attributes to the elements while keeping the single lookup. It acts like a sealed class (sort of) in that you cannot add any element that isn't defined in the enum.

Any attributes you add to the enum are available to use, like atomic number, weight, full name, or whatever else you want to add.

val e = Element.valueOf("Ne")
println("Element is ${e.full_name} - number ${e.num}")

// or

val e = Element.fromSymbol("Ne")
println("Element is ${e.full_name} - number ${e.num}")


// or 

val e = Element.fromNumber(10)
println("Element is ${e.full_name} - number ${e.num}")

Using the companion object getters instead of Element.valueOf also lets you do things like make the retrieval case-insensitive

fun fromSymbol(sym: String): Element {
    return values().firstOrNull { it.name.lowercase() == sym.lowercase() } ?: MISSING
}

Localization

The enum approach could also be easily adapted in the future if you wanted to localize the element names. You could put the localized names in strings.xml files and set a resource ID in the enum instead of a raw string

enum class Element( val name_id: Int, val num: Int) {
    Ne(R.string.neon,10),
    Fe(R.string.iron,26);
    
    fun getName(ctx: Context): String {
        return ctx.getString(name_id)
    }

    companion object {
        //...
    }
}
val e = Element.fromSymbol("Ne")
println("Name is ${e.getName(ctx)}")
Tyler V
  • 9,694
  • 3
  • 26
  • 52
1

Sealed class would not be a better approach for this, because you may have to create a class within sealed class for every element. That is not recommended if you have many classes. Same thing would applicable to enums too. When condition will be fine for your case.

Alternatively, you can store it in hashmap as key value pairs. And get element by the Key.

var elements = hashMapOf(
    "Ne" to "Neon",
    "Ar" to "Argon",
    "Kr" to "Krypton",
    "Xe" to "Xenon",
    "Rn" to "Radon",
    "Br" to "Bromine"
)
val item = elements["as"]?:""

If element not present in map, ?: operator(elvis) would give empty string.

Gowtham K K
  • 3,123
  • 1
  • 11
  • 29
1

A sealed class isn't going to really help you with this (you may have other reasons to do that though). You still need some way to convert the short name into the long name. There's two ways to do that:

1)Use a class with the short name and long name in it. Walk that list until you find a match. This is an O(n) operation.

2)Keep a map of short name->long name (or short name->class which has a long name). Then you just do a map lookup. This is an O(1) operation.

What you want is the second one. You can still use a sealed class in there if that gives you other advantages, but you want the map for the fast lookup.

Gabe Sechan
  • 90,003
  • 9
  • 87
  • 127