How to find object in list which contains specific value? - android

I have a list with objects. Every object has list with String
I want to find a object where any value from List is equal to that value.
val opinionsWithPhotos = state.opinionList.value?.filter { it.attachedPhotos != null }
val specificObject = opinionsWithPhotos?.first { it.attachedPhotos?.find { it == "myValue" } }
I don't know how to iterate over list of strings in every single object and find specific item.

i assume that your data to be like this
data class Foo(val photos:List<String>,...)
val listObj = listOf(Foo(listOf("string1", "string2", "string3", ...), ...)
then if you want to find an object where any value from the inner List is equal to your desired value , you could do like this :
// using any
val output1 : Foo? = listObj.find { foo : Foo ->
foo.photos.any { it == "myValue" }
}
// or using contains
val output2 : Foo? = listObj.find { foo : Foo ->
foo.photos.contains("myValue")
}

Related

Kotlin ActivityMainBinding build id in for loop

I have a problem. In my xml file, I have multiple textViews with the following id's:
txtRow1Column1
txtRow1Column2
txtRow2Column1
txtRow2Column2
txtRow3Column1
txtRow3Column2
txtRow4Column1
txtRow4Column2
Now I filled an array with random numbers between 1..2 and using the 1 or 2 I want to add a text in the textview from the resources. For that I already have the following function:
private fun startNewRound() {
// Define the array for (row, column, value)
val rows = arrayOf<Array<Int>>();
// Fill the array for each row and for each column
for (row in 1..4) {
rows.set(row, emptyArray())
for (column in 1..2) {
rows[row].plus((1..2).random())
}
}
// Show the correct text using the filled array
for (columns in rows) {
for (value in columns) {
val id = "txtRow" + rows.indexOf(columns) + "Column" + (columns.indexOf(value) + 1)
when (value) {
1 -> binding."ID HERE".text = getString(R.string.strTrue)
2 -> binding."ID HERE".text = getString(R.string.strFalse)
}
}
}
}
Currently I have added "ID HERE" instead of the id of the view, because I need to use a string as id to bind to the view. My question is: How can I use that string to find the correct id and be able to bind to that specific view?
Side note, the way you're building that 2D array looks like O(n^2), even if it wasn't going to throw IndexOutOfBoundsExceptions.
The way you're getting the column index won't work. Suppose you have the value 1 twice in a column. indexOf is going to return 0 both times because it returns the first valid match. Also, using indexOf inside your 2D loop results in O(n^3) complexity when it could just be O(n).
As to your problem, you can use Resources.getIdentifier() to find the Int ID of a view so you can find the view using findViewById. The alternative would be to use reflection on the binding class to find a field with that name, but that's uglier.
fun <T> View.getChildWithName(name: String): T {
val id = context.resources.getIdentifier("id", name, context.packageName)
return findViewById(id)
}
private fun startNewRound() {
val rows = Array(4) { Array(2) { (1..2).random() } }
rows.forEachIndexed { rowIndex, row ->
row.forEachIndexed { colIndex, value ->
val id = "txtRow${rowIndex + 1}Column${colIndex + 1}"
val view = binding.root.getChildWithName<TextView>(id)
view.text = when (value) {
1 -> getString(R.string.strTrue)
2 -> getString(R.string.strFalse)
}
}
}
}

How to update object value in MutableList?

I have a MutableList in my Android project where i'm adding an object called Articolo, then when a new item is added to that list i need to check if one item with same ID exist and if it does i need to update it's quantity.
The issue is that i'm trying to use MutableList.find to find the object with the same ID and when i find it i'm simply add the quantity to existing quantity but instead it remains immutable.
Here is my Articolo.kt
data class Articolo(var barcode: String, var qta: Int) {
constructor() : this ("", 0)
}
And here is my function where i'm adding data to MutableList
private var articoli = mutableListOf<Articolo>()
private fun addBarcode(barcode: String, qta: Int) {
if (barcode.isEmpty()) {
txtBarcode.requestFocus()
return;
}
articoli.find{
it.barcode == barcode
}?.qta?.plus(qta) ?:
articoli.add(Articolo(barcode, qta))
}
So if i add the first object like barcode: 1111, qty: 1 and then another same object instead of having one element array with qty 2 i still have qty 1..
That's because .plus(Int) returns a new value. You're not changing the property.
Instead you should do:
fun addBarcode(barcode: String, qta: Int) {
val existant = articoli.find { it.barcode == barcode }
if (existant != null) existant.qta += qta
else articoli.add(Articolo(barcode, qta))
}
#VaiTon86 has the answer (you're not actually changing the value in the Articolo object) but really, you should probably be using a Map here anyway:
maximum one of each item
lookup by some value (barcode)
that's a map!
There's a few ways you could implement it, here's one:
val articoli = mutableMapOf<String, Articolo>()
private fun addBarcode(barcode: String, qta: Int) {
articoli.getOrPut(barcode) { Articolo(barcode, 0) }
.let { it.qta += qta }
}
So the getOrPut just adds a new zero-quantity Articolo entry if there isn't already one, and then you add qta to what's already there for that entry.

Android implement text completion on swipe

How can I implement text completion,Like Gmail's smart compose?
I've an edit text where the user enters server address and I want to detect when they start typing the domain suffix and suggest completion.
Thanks.
First you need an algorithm to get suggestion from a given dictionary.
I've created a simple class named SuggestionManager to get suggestion from a given dictionary for a string input. Instead of returning the full match, it'll only return the remaining part of the given input. Below given a simple unit test along with full source code of the class. You can also go here to run the test online.
SuggestionManager.kt
class SuggestionManager(private val dictionary: Array<String>) {
companion object {
private val WORD_SPLIT_REGEX = Regex("[^A-Za-z0-9'\\-]")
/**
* To get reversed list
*/
private fun getReversedList(list: List<String>): MutableSet<String> {
val reversed = mutableSetOf<String>()
for (item in list.withIndex()) {
if (item.index != 0) {
val rev = list.subList(list.size - item.index, list.size).joinToString(" ")
reversed.add(rev)
}
}
// finally, add the full string
reversed.add(list.joinToString(" "))
return reversed
}
}
fun getSuggestionFor(_text: String?): String? {
var text = _text
// empty text
if (text.isNullOrBlank()) {
return null
}
// Getting last line only
if (text.contains("\n")) {
text = text.split("\n").last()
if (text.trim().isEmpty()) {
return null
}
}
// Splitting words by space
val words = text.split(WORD_SPLIT_REGEX).filter { it.isNotBlank() }
// Getting last word
val lastWord = if (text.endsWith(" ")) "${words.last()} " else words.last()
// Matching if last word exist in any dictionary
val suggestions = mutableSetOf<String>()
for (dicItem in dictionary) {
if (dicItem.contains(lastWord, true)) {
// Storing founded matches
suggestions.add(dicItem)
}
}
// Reverse ordering split-ed words
val pyramidWords = getReversedList(words)
val matchList = mutableListOf<String>()
for (pw in pyramidWords) {
for (sug in suggestions) {
// Storing suggestions starts with reversed word
if (sug.startsWith(pw, true)) {
matchList.add("$pw:$sug")
}
}
}
// Checking if second level match is not empty
if (matchList.isNotEmpty()) {
// Ordering by matched reversed word - (largest key first)
matchList.sortBy { -it.split(":")[0].length }
// Looping through in ascending order
for (m in matchList) {
val match = m.split(":")
val selPw: String = match[0]
var selSug: String = match.subList(1, match.size).joinToString(":")
// trimming to
selSug = selSug.replace(selPw, "", true)
if (text.endsWith(" ")) {
selSug = selSug.trim()
}
return selSug
}
}
return null
}
}
Unit Test
class SuggestionManagerUrlTest {
private val suggestionManager by lazy {
val dictionary = arrayOf(
"google.com",
"facebook.com",
"gmail.com",
"yahoo.com",
"192.168.354.45"
)
SuggestionManager(dictionary)
}
#Test
fun test() {
// null of empty and null input
assertNull(suggestionManager.getSuggestionFor(null))
assertNull(suggestionManager.getSuggestionFor(""))
// matching
assertEquals("ogle.com", suggestionManager.getSuggestionFor("go"))
assertEquals("book.com", suggestionManager.getSuggestionFor("face"))
assertEquals(".168.354.45", suggestionManager.getSuggestionFor("192"))
// no match
assertNull(suggestionManager.getSuggestionFor("somesite"))
}
}
Then, you'll have to set text in EditText with two colors. One for input and other for the suggestion. You may use the Html.fromHtml method to do this.
val text = "<font color=#cc0029>$input</font> <font color=#ffcc00>$suggestion</font>";
yourEditText.setText(Html.fromHtml(text));
Combining these two aspects, you can create a custom EditText.

Change a value in mutable list in Kotlin

I got this mutablelist:
[Videos(id=4, yt_id=yRPUkDjwr1A, title=test4, likes=0, kat=pranks, ilike=false), Videos(id=3, yt_id=WkyUU9ZDUto, title=test3, likes=0, kat=pranks, ilike=false), Videos(id=2, yt_id=B_X9OQqtduE, title=test2, likes=0, kat=animals, ilike=false), Videos(id=1, yt_id=ywaKlGNiv80, title=test1, likes=0, kat=animals, ilike=false)]
How can I change ilike to true where id is 2
This is what I've tried:
for (i in 0 until vids!!.size) {
Log.d("lets", vids!!.get(i).title)
if(vids!!.get(i).id == 2){
vids!!.get(i).ilike = true
}
}
You can use find function to find the element with id = 2 and change its property:
vids?.find { it.id == 2 }?.iLike = true
Note: it is a good practice to use question mark if the property is nullable and you unsure whether it is null or not.
If you expect few items (maybe 1 or 2?) to be affected,
you can filter the list and then change iLike of the filtered items:
vids!!.filter { it.id == 2 }.forEach { it.iLike = true }
Try this, I'm assuming your Videos structure is a data class defined somewhat like so. data class Videos(val id: Int, val yt_id: String, val title: String, val likes: Int, val kat: String, val ilike: Boolean)
list.forEachIndexed { index, video ->
video.takeIf { it.id == 2}?.let {
list[index] = it.copy(ilike = true)
}
}
I had to change several properties and I had a need to hold the changed object. Therefore following approach worked better for me:
//First, find the position of the video in the list
val videoPosition= list.indexOfFirst {
it.id == 2
}
//Now get your video by position and make changes
val updatedVideo = list[videoPosition].apply {
//Make all changes you need here
ilike = true
//...
}
//Finally, replace updated video into your list.
list[videoPosition] = updatedVideo
Use set to replace the object if you don't want to use predicates or iteration
Eg.
val video = (...,read = true) //or however you are getting the current model
val updatedVideo = video
updatedVideo.read = true
vids[vids.indexOf(video)] = updatedVideo

Transform directory to object in Kotlin

I'm trying to parse some files this way:
File(tentConfig.getPathRepository())
.walkTopDown()
.forEach { file -> processFile(file) }
the path of this file is: /communications/email/begginer/.file
I have to convert that path to object like this:
my communications should be my category, email should be a subcategory of communications and beginner subcategory of email.
my process method is responsible to serialize this path to object but I'm pretty sure there is a better solution.
private fun processCategory(currentFile: File) {
val listOfDirectory = currentFile.path.split("/".toRegex())
listOfDirectory.forEachIndexed { index, folderName ->
if (index == 0) {
val currentCategory = parseYmlFile(currentFile, Category::class)
lesson.categories.forEach { itCategory ->
if (itCategory.title != currentCategory.title) lesson.categories.add(currentCategory)
}
} else {
val subCategory = parseYmlFile(currentFile, Category::class)
lesson.categories[subCategory.index].subcategories.add(subCategory)
}
}
}
For the sake of demo/testing purposes, my implementation of Category might be different from yours. Here's the one I was using:
inner class Category(val s: String, var subCategory: Category? = null)
Now that being said, here's a little function that will loop through the path of the given File, and construct a Category hierarchy, placing each element in the right order:
private fun processCategory(currentFile: File): Category? {
val listOfDirectory = currentFile.path.split("/".toRegex())
//The root category (in your example, communications)
var rootCategory: Category? = null
//A reminder of the current Category, so we can attach the next one to it
var currentCategory: Category? = null
listOfDirectory.forEach {
if (rootCategory == null) {
//First element, so I need to create the root category
rootCategory = Category(it)
currentCategory = rootCategory
} else {
//Other elements are simply created
val nextCategory = Category(it)
//Added as a subCategory of the previous category
currentCategory!!.subCategory = nextCategory
//And we progress within the chain
currentCategory = nextCategory
}
}
//In the end, my root category will contain :
// Category("communications", Category("email", Category("Beginner", null)))
return rootCategory
}
You can surely adapt this code to your needs, by replacing the constructor that I'm using with your YmlParser

Categories

Resources