I am new using Kotlin and have implemented a recyvclerview list and now I'm using putExtra() and getStringExtra() intents to open in the same activities to avoid creating too many activities. So far it is working fine but I'm noticing my list is getting to long, and I'm planning on adding more. Is there a way to create a list of the numbers and call it into the intent? or maybe create another file class with the numbers and call it into the intent? If so how can it be done?
val intent = Intent(this, HNSPSFOC1::class.java)
val drinks = intent.getStringExtra("Drinks").toString()
val food = intent.getStringExtra("FastFood").toString()
when {
drinks == "1" -> { intent.putExtra("CoffeeShops","1")}
drinks == "2" -> { intent.putExtra("CoffeeShops","2")}
drinks== "3" -> { intent.putExtra("CoffeeShops","3")}
drinks == "4" -> { intent.putExtra("CoffeeShops","4")}
drinks == "5" -> { intent.putExtra("CoffeeShops","5")}
drinks == "6" -> { intent.putExtra("CoffeeShops","6")}
drinks == "7" -> { intent.putExtra("CoffeeShops","7")}
drinks == "8" -> { intent.putExtra("CoffeeShops","8")}
drinks == "9" -> { intent.putExtra("CoffeeShops","9")}
drinks == "10" -> { intent.putExtra("CoffeeShops","10")}
drinks == "11" -> { intent.putExtra("CoffeeShops","11")}
drinks == "12" -> { intent.putExtra("CoffeeShops","12")}
drinks == "13" -> { intent.putExtra("CoffeeShops","13")}
drinks == "14" -> { intent.putExtra("CoffeeShops","14")}
drinks == "15" -> { intent.putExtra("CoffeeShops","15")}
drinks == "16" -> { intent.putExtra("CoffeeShops","16")}
drinks == "17" -> { intent.putExtra("CoffeeShops","17")}
drinks == "18" -> { intent.putExtra("CoffeeShops","18")}
drinks == "19" -> { intent.putExtra("CoffeeShops","19")}
drinks == "20" -> { intent.putExtra("CoffeeShops","20")}
food == "1" -> {intent.putExtra("FastFood","1")}
food == "2" -> { intent.putExtra("FastFood","2")}
food== "3" -> { intent.putExtra("FastFood","3")}
food == "4" -> { intent.putExtra("FastFood","4")}
food == "5" -> { intent.putExtra("FastFood","5")}
food == "6" -> { intent.putExtra("FastFood","6")}
food == "7" -> { intent.putExtra("FastFood","7")}
food == "8" -> { intent.putExtra("FastFood","8")}
food == "9" -> { intent.putExtra("FastFood","9")}
food == "10" -> { intent.putExtra("FastFood","10")}
food == "11" -> { intent.putExtra("FastFood","11")}
food == "12" -> { intent.putExtra("FastFood","12")}
food == "13" -> { intent.putExtra("FastFood","13")}
food == "14" -> { intent.putExtra("FastFood","14")}
food == "15" -> { intent.putExtra("FastFood","15")}
food == "16" -> { intent.putExtra("FastFood","16")}
food == "17" -> { intent.putExtra("FastFood","17")}
food == "18" -> { intent.putExtra("FastFood","18")}
food == "19" -> { intent.putExtra("FastFood","19")}
food == "20" -> { intent.putExtra("FastFood","20")}
}
Your drinks variable is already string. You don't have to use when statement.It is unnecessary. You can pass directly this variable like this.
intent.putExtra("Coffee Shops", drinks)
I'm just posting this as an answer so I can explain it without the comment length limitation, so don't mark it as the correct one or anything! And uh it's a bit longer than I intended but hopefully it helps you understand coding in general a bit more
So you've edited your question with a longer when block that handles a food variable as well as a drinks one. There's a big problem with this but I'll get to that in a bit. Let's just look at drinks for now:
when {
drinks == "1" -> { intent.putExtra("CoffeeShops","1")}
drinks == "2" -> { intent.putExtra("CoffeeShops","2")}
drinks == "3" -> { intent.putExtra("CoffeeShops","3")}
...
}
The first thing you should notice is that you're doing pretty much the same thing over and over. It wasn't fun writing that all out, right? Plus there's the possibility you could make a mistake and use the wrong value, or if you had to change this in the future, you'd have to edit every line and possibly make an error there. There's always the chance to make an error, but that chance grows when you have a lot of repetitive work like this.
Just as a general rule, if you find yourself repeating the same thing, that's usually a sign that you should write a bit of logic that works out the correct value or correct action based on some values, and just does the appropriate thing.
So what would that look like here? Well what's in common for every line?
always calls putExtra on the intent variable
always passes "CoffeeShops" as the key
uses a changing value
Really, the only unique bit of data here is the value parameter being passed to putExtra. So instead, you could use your when block to just work out what that value should be, and then whatever the result, use that in your putExtra call:
val drinksValue = when {
drinks == "1" -> "1"
drinks == "2" -> "2"
drinks == "3" -> "3"
...
}
intent.putExtra("CoffeeShops", drinksValue)
Already you should be able to see how much better that is - the intent.putExtra call is only written once, the "CoffeeShops" key is only written once, it's way less effort to write and maintain and easier to spot potential bugs because you only have to look at one thing.
Hopefully you've also noticed something about the pattern on the left of the when block. There's another way to write that actually - this is more about learning language features than can help you than just structuring your logic so you don't have to repeat yourself, so don't worry if you didn't know it, but it should make something clearer:
// putting a variable in parentheses here makes it the value that all the
// branches are checked against - so you don't need to write "drinks =="
val drinksValue = when(drinks) {
"1" -> "1"
"2" -> "2"
...
}
So now it's clearer what's happening - if the value of drinks is "1", our result is "1". If the value is "2", our result is "2", etc.
That's... definitely repeating ourselves! We've turned all the putExtra calls into a single line, but we have a lot of lines saying if this value is "1", use "1" - we can just use the drinks value as-is, right? No need for the when checking every case
intent.putExtra("CoffeeShops", drinks)
Now it literally is a single line. You can wrap that in some code to validate the range of correct values, which is also kinda what the when was doing, but there's a pattern here - and if we're smart, we can work out a way to check if it's valid. It's a String which makes it a little harder, but all the valid values are a range of numbers, so we can do some conversion:
// Try to parse 'drinks' as an integer and check if it's in the range 1 to 20
// If it can't be parsed as an Int (say it's "a" for some reason) the result
// will be null, and 'null in (1..20)' returns false - works for all invalid values!
if (drinks.toIntOrNull() in (1..20)) {
intent.putExtra("CoffeeShops", drinks)
}
So you added another food variable you want to handle as well, right? Well, that's completely separate from drinks, so you want to do both. And if you look at your when block... it's exactly the same logic, right?
when {
drinks == "1" -> { intent.putExtra("CoffeeShops","1") }
...
food == "1" -> { intent.putExtra("FastFood","1") }
}
Same thing, it's just the key is changing now, and obviously the value you're putting in the extra is from food. We can just do the exact same thing as before - so now you've got:
if (drinks.toIntOrNull() in (1..20)) {
intent.putExtra("CoffeeShops", drinks)
}
// same thing but with 'food' - validate the value, insert with appropriate key
if (food.toIntOrNull() in (1..20)) {
intent.putExtra("FastFood", food)
}
You're handling drinks, and then you're handling food. But hey - there's repetition here too, right? Not a lot, and this is where it becomes a judgement call, but if you had more of these variables, you'd probably want to avoid the repetition. So we could break out this logic into a separate function that has all the common stuff, and just pass in the things that change:
fun tryInsertValue(value: Int, key: String) {
if (value.toIntOrNull() in (1..20)) {
intent.putExtra(key, value)
}
}
Then you can call it with
tryInsertValue(drinks, "CoffeeShops")
tryInsertValue(food, "FastFood")
I'm assuming some things here - the valid number range is always from 1 to 20, the function can see intent - but you could always pass that intent in as a parameter, or pass in a valid range to check. This can get as involved as you want, and it's where you have to decide if it's worth it or not in your particular case, but hopefully you get the general idea and a better sense of how to approach these things. Break stuff down, look for commonalities, and avoid repeating yourself!
This bit's important
So the other problem with your current code is that when blocks go through each case in order, looking for the first match, and then they evaluate the branch for that match. Then the when block exits.
Say your drinks value is "2" and food is "1". First the when block checks if drinks is "1" - it isn't. Then it moves to the next check - is drinks "2"? Yes it is, so you do intent.putExtra("CoffeeShops","2") and exit the block.
When does it ever get to checking the food == "1" case? It doesn't. It will always hit the drinks == "2" one first, which will be true, and that's it, end of block, no more checks. So if you did write out every case, you'd need a separate when for all the drinks stuff, and another one for all the food stuff. So they both run, completely independently. The reason to put them all in one block is if you wanted it to act that way, and only set the food value if drinks didn't match anything. Sometimes you want that, but it seems like you don't here
whew
Related
I have a long list of objects around 300, with each object in the list having this data structure:
case class MyObject(id: String,
name: String,
colour: String,
price: Int
height: Int
width: Int,
desc: String)
I can’t work out what is the best way to go through the list and for each object remove any other object that has the same name, colour, price, height and width. Note that this isn’t a simple dedupe as the ids and desc can be different. The input and output need to remain List[MyObject] and I do not know beforehand which objects are the duplicated ones.
This is my initial solution which works, but not sure its the most efficient way of doing it when it comes to dealing with large list.
def removeDuplicates(originalList: List[MyObject]): List[MyObject] = {
def loop(remaining: List[MyObject], acc: List[MyObject]): List[MyObject] = {
remaining match {
case head :: tail =>
val listOfDuplicates = tail.filter{ x =>
x.name == head.name &&
x.colour == head.colour &&
x.price == head.price &&
x.height == head.height &&
x.width == head.width
}
val deDupedTail = tail.filter(!listOfDuplicates.contains(_))
loop(deDupedTail, acc ::: listOfDuplicates)
case Nil => acc
}
}
val listOfDuplicateObjects = loop(originalList, List())
originalList.filter(!listOfDuplicateObjects.contains(_))
}
Not sure if it's most efficent, but IMHO it's elegant:
originalList.distinctBy(o => (o.name, o.colour, o.price, o.height, o.width))
In F#, assume I have a person record as:
type person =
{ LastName: string option
BirthDate: System.DateTime option }
Now, I want to create a list of 100 persons (this fails. Both name and The System.DateTime(...) is incorrect):
let people = [for a in 1 .. 100
do yield {
LastName= Some "LastName"+a
BirthDate = System.DateTime(2012,11,27)
}]
How is this done?
TIA
There are two separate issues with the code, but your general approach is good!
First, Some "LastName"+a is interpereted as (Some "LastName")+a, which is not the right parenthesization. Also a is an int which cannot be automatically turned into a string, so you need to explicitly convert it. The correct version is Some("LastName" + string a).
Second, System.DateTime(2012,11,27) is DateTime, but you need an option. You can fix this just by adding Some and the right parentheses, i.e. Some(System.DateTime(2012,11,27)).
As a bonus, you can reduce do yield to -> (this is just a syntactic sugar to make this kind of thing shorter). I would write:
open System
let people =
[ for a in 1 .. 100 ->
{ LastName= Some ("LastName"+string a)
BirthDate = Some(DateTime(2012,11,27)) } ]
Lets say I have a list of Vendors: Asda, Tesco, Spar.
And I have a list of Sources (or suppliers in this analogy): Kellogg, Cadbury, Nestle, Johnsons, Pampers, Simple, etc. (there is a defined list of around 20).
Elsewhere in the flow of data. I am returning a result, which is Yes/No for each vendor, for multiple different things.
For example: Asda: ukOnly = "Yes"; Spar: ukOnly = "No" etc.
In this specific section, I am collating results.
Mostly it doesn't matter if the sources from the vendors overlap. So I can just say:
function concatResults(x) -- concats the result of "x" for each vendor
local pathAsda = *this is where I call the path location specific to Asda*
local pathTesco = *this is where I call the path location specific to Tesco*
local pathSpar = *this is where I call the path location specific to Spar*
if (pathAsda == "Yes" or pathTesco == "Yes" or pathSpar == "Yes") then
return "Yes"
else
return "No"
end
end
ukOnlyAgr = concatResults("ukOnly")
Great!
Now, say I want to do something more comple.
I want to know how many unique suppliers are providing chocolate and cereal. The example below is being used further up the process to produce a fact suppliesSweet, only if there is at least two sources (suppliers) involved and they must be at least supplying chocolate. This will be done for each vendor separately (please assume I have already defined my variables based on the input data:
if (suppliesChoc > 0 and suppliesCereal > 0 and numSources > 1) or (suppliesChoc > 1) then
then suppliesSweet = "Yes"
else suppliesSweet = "No"
end
Not a problem yet.
The issue comes when I try to aggregate these results across vendors (as I did before with ukOnly).
I already have the following function being used:
table.contains = function(t, value) -- Finds if "value" exists inside the table "t"
for index = 1, #t do
if t[index] == value then
return index
end
end
end
And was thinking of creating this:
table.overlap = function(t,g) -- Finds if tables "g" and "t" have any overlapping values
for i=1,#t do
if table.contains(g,t[i]) then
return true
else
return false
end
end
end
But I'm just not sure where to go from there.
You can assume I have already got a list of unique sources for each vendor and I don't mind if we're over restrictive. I.e. if any sources overlap between the two vendors, that would invalidate the entire result.
You can also assume I have each "fact": suppliesChoc, suppliesCereal, numSources and suppliesSweet for each vendor returned separately.
I believe your looking for the intersection of two sets.
https://en.wikipedia.org/wiki/Intersection_(set_theory)
One set being your vendor's suppliers and the other being the suppliers who supply sweets.
local vendors = {
Asda = {Kellogg = true, Cadbury = true, Nestle = true, Johnsons = true, Pampers = true, Simple = true},
Tesco = {Kellogg = true, Cadbury = true, Nestle = true, Johnsons = true},
Spar ={Nestle = true, Johnsons = true, Pampers = true, Simple = true}
}
function intersection(s1, s2)
local output = {}
for key in pairs(s1) do
output[#output + 1] = s2[key]
end
return output
end
local sweetSuppliers = {Kellogg = true, Cadbury = true, Nestle = true}
for name, suppliers in pairs(vendors) do
local result = intersection(sweetSuppliers, suppliers)
print(name .. " has " .. #result .. " sweets suppliers")
end
Here are some examples of a libraries for handling sets:
odkr's properset.lua
Windower's sets.lua
Both can give you an idea of how you can use sets to accomplish things like intersection, and much more
I have an app that is reading an ingredients list. At this point I've already retrieved a list of the 2500 most common ingredients. So I've got a list of, say 10 ingredients as strings, and a list of 2500 ingredients, with names as well as other properties. If an ingredient in this list of strings matches the name of an ingredient in the list of ingredients, I'd like to add it to another list third list, of ingredients that exist. The only way I know how to do that is with basically a for loop.
I'd do it as
fun compareLists(listOfIng: List<String>): List<ListIngredientsQuery.Item> {
var returnList = mutableListOf<ListIngredientsQuery.Item>()
for (ing in listOfIng) {
for (serverIngredient in MyApp.metaIngredientList!!) {
if (serverIngredient.name() == ing) {
returnList!!.add(serverIngredient)
}
}
}
return returnList
}
Which would technically work, but I have to imagine there's a better, faster way than iterating over 2500 items, as many times as there are Ingredients in an Ingredient list. What is the like, proper, preferred by real developers, way of doing this.
As each ingredient name is unique, you can use hash map for storing your 2500 ingredients with its name as the key. This way you do not need to loop over that huge collection any more, but just look thing up by the name and let the hash map deal with it.
To put some code to what Marcin said, here is what I would do:
fun compareLists(listOfIng: List<String>) =
MyApp.metaIngredientList!!
.associateBy { it.name() }
.let { metaIngredientMap -> listOfIng.mapNotNull { metaIngredientMap[it] }}
Or if we wanna avoid using !!
fun compareLists(listOfIng: List<String) =
MyApp.metaIngredientList
?.associateBy { it.name() }
?.let { metaIngredientMap -> listOfIng.mapNotNull { metaIngredientMap[it] }}
?: emptyList<ListIngredientQuery.Item>()
Of course, ideally, you would want that MyApp.metaIngredientList to be already a Map and not convert it into a Map for each operation
I'm working on a game and am stuck on a part where a key is supposed to unlock the door of a room. Now, after some searching, I've come across that i can't call a variable that exists in one function, into another function: as i have been trying to by setting i.e.key_picked = True under a conditional in the kitchen function. And then, using a conditional in the room function, with the key_pickedin a Boolean expression.
So, how do i work around this?
def kitchen(already_there=False):
if choice02_key == "pick key" or choice02_key == "0" or choice02_key == "key":
print("---------------------------------------------------------")
print "You picked the key. It probably unlocks some door."
key_picked = True
kitchen(already_there=True)
def room01(already_there=False):
if key_pick == True:
print("---------------------------------------------------------")
print "You unlocked the room using the key."
else:
print "This room is locked."
entrance_hall(already_there=True)
You can pass the variable in a parameter. For example:
Define keyPicked in room01.
Call kitchen(already_there, keyPicked) from room01.
Make the assignment you want.
Then, you will have the value you want in keyPicked.
For example, imagine I have a function that add 10 to a number. This will be better to return the value, but it is just to show you how can you do it.
def add_ten(number):
number = number + 10
def main():
number = 5
print('Number is:', number)
add_ten(number)
print('Number is:', number)
Output:
Number is: 5
Number is: 15