XMLPullParser returns only one element - kotlin

I could not parse my XML file, it returns only one element instead of 4
Here's my XML file
<Quizzs>
<Quizz type="A">...</Quizz>
<Quizz type="B">...</Quizz>
<Quizz type="C">...</Quizz>
<Quizz type="D">...</Quizz>
</Quizzs>
It returns only the last one "D"
while (eventType != XmlPullParser.END_DOCUMENT) {
var eltName: String? = null
when (eventType) {
XmlPullParser.START_TAG -> {
eltName = parser.name
if ("Quizzs" == eltName) {
currentQuizz = Quizz()
quizz.add(currentQuizz)
} else if (currentQuizz != null) {
if ("Quizz" == eltName) {
currentQuizz.type = parser.getAttributeValue(null, "type")
}
}
}
}
eventType = parser.next()
}
printPlayers(quizz)
}

You need to .add() something to your currentQuizz for each "Quizz". With currentQuizz.type = ... you just overwrite each previous "Quizz" with the current one, so you end up with only the last one, which is D.
I think you are confused by your own code. For the "Quizzs" tag you create a Quizz() object instead of a QuizzList() object or something similar. It is for the "Quizz" tag you should create a new Quizz() object each time. And then you should add that object to your QuizzList.

Related

Using string translation in adapter return numbers

I am trying to use translation string in my adapter but it returns numbers instead
if(currentItem.budget != null){
holder.budget.text = "$ " + currentItem.budget.format()
} else {
holder.budget.text = R.string.open_to_suggestions.toString()
}
R.string.open_to_suggestions.toString() supposed to return string text Open to suggestions but it returns numbers such as 2131755113 not sure why! any idea?
To show the string resource you must use context.getString()
if(currentItem.budget != null) {
holder.budget.text = "$ " + currentItem.budget.format()
} else {
val context = holder.itemView.context
holder.budget.text = context.getString(R.string.open_to_suggestions)
}
Please take a look at the definition of getString here

use map function on condition in kotlin

I have a list of items and I want to edit its values before using it. I am using the map function to update each item in it. But the catch here is, I want to only update the items when the list size is 1. I want to return the list as it is if the size is larger than 1. How can I achieve this?
myList.map {
if(resources.getBoolean(R.bool.is_tablet) && it.itemList.size<6 && it.layerType == DOUBLE_LIST) {
it.layerType = SINGLE_LIST_AUTO
it.itemList.forEach {sectionItem->
sectionItem.layerType = SINGLE_LIST_AUTO
}
it
}else{
it
}
}
You can try using filter before map:
.filter { it.itemList.size == 1 }
I am assuming you want to modify the items in your list only if some conditions are met else return the same list unmodified.
You can consider using takeIf { } for this scenario if you desire to add some syntactic sugar
fun updateItemsInMyList(myList:List<SomeClass>): List<SomeClass> {
return myList
.takeIf {
// condition to modify items in your list
it.size > 1 && otherConditions
}
?.apply {
//update your items inside the list
}
?: myList // return the unmodified list if conditions are not met
}
If I understand your question correctly, you want to check if myList contains only one value else, you want update the values and return it. You could do something along the following lines,
myList.singleOrNull ?: myList.map {
if(resources.getBoolean(R.bool.is_tablet) && it.itemList.size<6 && it.layerType == DOUBLE_LIST) {
it.layerType = SINGLE_LIST_AUTO
it.itemList.forEach {sectionItem->
sectionItem.layerType = SINGLE_LIST_AUTO
}
it
}else{
it
}
}
return myList
Basically, check if there's only a single value in the list, if so, then return the value. In the case that there isn't (you get null), then you can map the value.

Is there a way to merge filter and map into single operation in Kotlin?

The below code will look for "=" and then split them. If there's no "=", filter them away first
myPairStr.asSequence()
.filter { it.contains("=") }
.map { it.split("=") }
However seeing that we have both
.filter { it.contains("=") }
.map { it.split("=") }
Wonder if there's a single operation that could combine the operation instead of doing it separately?
You can use mapNotNull instead of map.
myPairStr.asSequence().mapNotNull { it.split("=").takeIf { it.size >= 2 } }
The takeIf function will return null if the size of the list returned by split method is 1 i.e. if = is not present in the string. And mapNotNull will take only non null values and put them in the list(which is finally returned).
In your case, this solution will work. In other scenarios, the implementation(to merge filter & map) may be different.
I see your point and under the hood split is also doing an indexOf-check to get the appropriate parts.
I do not know of any such function supporting both operations in a single one, even though such a function would basically just be similar to what we have already for the private fun split-implementation.
So if you really want both in one step (and require that functionality more often), you may want to implement your own splitOrNull-function, basically copying the current (private) split-implementation and adapting mainly 3 parts of it (the return type List<String>?, a condition if indexOf delivers a -1, we just return null; and some default values to make it easily usable (ignoreCase=false, limit=0); marked the changes with // added or // changed):
fun CharSequence.splitOrNull(delimiter: String, ignoreCase: Boolean = false, limit: Int = 0): List<String>? { // changed
require(limit >= 0, { "Limit must be non-negative, but was $limit." })
var currentOffset = 0
var nextIndex = indexOf(delimiter, currentOffset, ignoreCase)
if (nextIndex == -1 || limit == 1) {
if (currentOffset == 0 && nextIndex == -1) // added
return null // added
return listOf(this.toString())
}
val isLimited = limit > 0
val result = ArrayList<String>(if (isLimited) limit.coerceAtMost(10) else 10)
do {
result.add(substring(currentOffset, nextIndex))
currentOffset = nextIndex + delimiter.length
// Do not search for next occurrence if we're reaching limit
if (isLimited && result.size == limit - 1) break
nextIndex = indexOf(delimiter, currentOffset, ignoreCase)
} while (nextIndex != -1)
result.add(substring(currentOffset, length))
return result
}
Having such a function in place you can then summarize both, the contains/indexOf and the split, into one call:
myPairStr.asSequence()
.mapNotNull {
it.splitOrNull("=") // or: it.splitOrNull("=", limit = 2)
}
Otherwise your current approach is already good enough. A variation of it would just be to check the size of the split after splitting it (basically removing the need to write contains('=') and just checking the expected size, e.g.:
myPairStr.asSequence()
.map { it.split('=') }
.filter { it.size > 1 }
If you want to split a $key=$value-formats, where value actually could contain additional =, you may want to use the following instead:
myPairStr.asSequence()
.map { it.split('=', limit = 2) }
.filter { it.size > 1 }
// .associate { (key, value) -> key to value }

How can i return different types from my method?

I have a method which returns an object (this._response) which is of type IResponse. I want to return this._response.details which is of type IDetails.
get response(): IResponse {
if (this._response.response.header.statusCode === "0000" || this._response.response.header.statusCode === "YHOO") {
return this._response.details; //IDetails
} else if (this._response.response.header.statusCode === "6621" || this._response.response.header.statusCode === "6622") {
this._response.response.isViewError = true;
} else {
this._response.response.showError = this._response.response.header;
}
return this._response //IResponse
}
[ts] Property 'details' does not exist on type 'IResponse'.
How can I return different types from method based on if condition?
You can use union types:
get response(): IDetails | IResponse {
...
}
Edit
The problem with returning a union is that you'll need to check for the type of the result every time you invoke the function.
So:
function isResponse(obj: IDetails | IResponse) obj is IResponse {
return obj && obj.response;
}
let value = myObj.response;
if (isResponse(value)) {
// value is IResponse
} else {
// value is IDetails
}
Another option is to simply not specify anything; the return types are automatically inferred:
the danger here is this allows you to return anything so there is no explicit guard, during development, against wrong return types.

two way mapping

Is any way, how to create two classes, which will be referenced both way and will use only one FK? This interest me in One-to-One as like as One-to-Many cases.
f.e.:
Class First: Entity
{
Second second;
}
Class Second: Entity
{
First first;
}
String TwoWayReference()
{
First Fir = new First();
Second Sec = new Second();
Fir.second = Sec; // I need it is equivalent to: Sec.first = Fir;
if (Sec.first == Fir)
return "Is any way how to do this code works and code return this string?";
else
return "Or it is impossible?"
}
simplest would be
class First : Entity
{
private Second second;
public virtual Second Second
{
get { return this.second; }
set {
if (value != null)
{
value.First = this;
this.second = value;
}
}
}
}
class Second : Entity
{
private First first;
public virtual First First
{
get { return this.first; }
set {
if (value != null && value.Second != this)
{
value.Second = this;
this.first = value;
}
}
}
}