Scala iterate through list except last element - scala

Using a for loop, how can I iterate through a list, with the ability to not iterate over the very last element in the list.
In my case, I would like to not iterate over the first element, and need to iterate through backwards, here is what I have:
for( thing <- things.reverse) {
//do some computation iterating over every element;
//break out of loop when first element is reached
}

You can drop the first item before you reverse it:
for(thing <- things.drop(1).reverse) {
}
For lists, drop(1) is the same as tail if the list is non-empty:
for(thing <- things.tail.reverse) {
}
or you could do the reverse first and use dropRight:
for(thing <- things.reverse.dropRight(1)) {
}
You can also use init if the list is non-empty:
for(thing <- things.reverse.init) {
}

As mentioned by Régis, for(thing <- things.tail.reverse) {}

Related

Flutter/Dart Compare multiple Lists and generate list with elements containd in all given lists

So here is my problem:
let's say we have two or more lists
List<SomeModel> one = [SomeModel, SomeModel, SomeModel],
List<SomeModel> two = [SomeModel, SomeModel, SomeModel, SomeModel, SomeModel, SomeModel]
what I want to do is compare these two/or more lists and make a new Listcontaining only those elements that exist in every list.
List<Fruits> one = [Banana, Apple, Cherry];
List<Fruits> two = [Cherry, Blueberry, Apple, Mango, Pineapple, Pear];
List<Fruits> three = [Cherry, Apple, Mango, Pineapple, Pear, Banana];
List<Fruits> getMatchingList(){
do something...
List<Fruits> matchingFruits = [Cherry, Apple];
return matchingFruits
}
I literally have no idea how to do this, especially with larger lists or with lists of unknown content.
If the number of lists is fixed you can just use the where function to filter items:
List<SomeModel> filteredList = one.where(item => two.contains(item))
.where(item => three.contains(item)).toList();
So your method will be:
List<Fruits> getMatchingList(List<Fruits> one, List<Fruits> two, List<Fruits> three){
return one.where(item => two.contains(item))
.where(item => three.contains(item))
.toList();
}
If you need an increasing number of lists to be processed, on the other side, just use:
List<Fruits> getMatchingList(List<List<Fruits>> listsToAnalyze){
Map<Fruits,integer> fruitCounter = new Map();
//expand is a method that flattern a list of lists in a single list
listsToAnalyze.expand(element => element).toList().forEach(element => {
//if the element exists, its counter is increased, otherwise is set to 1
fruitCounter.update(element, (count) => count+1, ()=> 1);
}
//remove results with counter < size of the list of lists
//supposing that an item can appear only once in each list, this means
//that the value appeared in all lists
fruitCounter.removeWhere((key,value)=> value<listsToAnalyze.lenght);
return fruitCounter.keys.toList();
}
In case an item can appear multiple times in a single list, do not expand the list and keep a List<Fruit> to control if an item was already inserted during each cycle on a list. Remember to empty the map at each new list.
List<Fruits> getMatchingList(List<List<Fruits>> listsToAnalyze){
Map<Fruits,integer> fruitCounter = new Map();
List<Fruits> fruitsInsertedThisRound= new List();
//cycle on each list alone
listsToAnalize.forEach(list => {
fruitsInsertedThisRound.clear(); //ensure that the list is empty at each cycle
list.forEach(el => {
//if the element exists, its counter is increased, otherwise is set to 1
if(!fruitsInsertedThisRound.contains(el){
//fruit still not inserted this round
fruitCounter.update(el, (count) => count+1, ()=> 1);
fruitsInsertedThisRound.add(el); //add the element to the list of inserted fruits
}
}
}
//remove results with counter < size of the list of lists
//this means that the value appeared in all lists
fruitCounter.removeWhere((key,value)=> value<listsToAnalyze.lenght);
return fruitCounter.keys.toList();
}
Merge both the list by removing duplicate
As Single Line
a.addAll(b.takeWhile((item)=> !a.contains(item)));
by looping
for(var item in a){
if(!b.contains(item)){
b.add(item);
}
}

Flutter Remove Multiple List Items In For Loop (Multiple Returns In A For Loop)

I have a list of food items. That has a category, subCategory1, subCategory2 ect.
When the user unselects an category item. They are also unselecting the subCategory1, subCatategory2 items etc as they are children of the original categogory item like this:
So I have a for loop that is running through looking for children category list elements and removing them like this:
// Remove a category item and all of its children
if (categoryType == "category") {
List<String> subCategory1Children = List<String>.from(
snapshot.data.documents[gridIndex]['subCategory1Children']);
// Remove the subcategory items
for (int i = 0; i < foodDrinkMenuElements['subCategory1'].length; i++) {
String subID = foodDrinkMenuElements['subCategory1'][i];
int removalIndex = _indexOfListGridElement('subCategory1', subID);
if (subCategory1Children.contains(subID)) {
_removeCategoryGridItem(removalIndex, subID, 'subCategory1');
}
}
//Remove the actual item being pressed
_removeCategoryGridItem(listIndex + 1, id, categoryType);
}
Which calls _removeCategoryGridItem() like this:
void _removeCategoryGridItem(int removalIndex, String id, String categoryType) {
_FoodandDrinkKey.currentState.removeItem(removalIndex,
(BuildContext context, Animation<double> animation) {
return _buildListItem(removalIndex, animation);
});
foodDrinkMenuElements[categoryType].remove(id);
}
The For loop is always finishing after 1 list item is removed. I assume this is because of the return statement in the function _removeCategoryGridItem.
I have seen other answers that say to put it in an list and run through the list but I don't see how that applies here.
Thanks for your help
The For loop is always finishing after 1 list item is removed. I assume this is because of the return statement in the function _removeCategoryGridItem.
No, generally the only way that a function could break out of a loop in a calling function is by throwing an exception.
I don't know what type foodDrinkMenuElements[categoryType] returns, but presuming that it's a List, Map, or Set, you cannot remove items from the collection while you iterating over the collection.
From the List documentation:
It is generally not allowed to modify the list's length (adding or removing elements) while an operation on the list is being performed.... Changing the list's length while it is being iterated ... will break the iteration.
There is similar language for Map and Set.
I have seen other answers that say to put it in an list and run through the list but I don't see how that applies here.
That is exactly what you should do: you should queue which items to remove and then process the queue to avoid mutating the same collection you're iterating over:
final pendingRemoves = List<void Function()>[];
// Remove the subcategory items
for (int i = 0; i < foodDrinkMenuElements['subCategory1'].length; i++) {
String subID = foodDrinkMenuElements['subCategory1'][i];
int removalIndex = _indexOfListGridElement('subCategory1', subID);
if (subCategory1Children.contains(subID)) {
pendingRemoves.add(() =>
_removeCategoryGridItem(removalIndex, subID, 'subCategory1'));
}
}
// Since an index is involved, you also need to remove in reverse order so that
// the queued indices still refer to the same elements.
for (pendingRemove in pendingRemoves.reversed) {
pendingRemove();
}
//Remove the actual item being pressed
_removeCategoryGridItem(listIndex + 1, id, categoryType);

How can you create a timer that works on a List in Rx?

I want to look for an entire list of items to be found before I complete and if that entire list isn't found, then an exception (a Timeout or custom one) is to be thrown. Like the built in Observable.timer() but instead of the test passing once the first item is emitted, I want it to require all of the items in a list to be found.
Here is an example. Let's say I have some test function that emits Observable<FoundNumber>. It looks like this:
var emittedList: List<String?> = listOf(null, "202", "302", "400")
data class FoundNumber(val numberId: String?)
fun scanNumbers(): Observable<FoundNumber> = Observable
.intervalRange(0,
emittedList.size.toLong(),
0,
1,
TimeUnit.SECONDS).map { index ->
FoundNumber(emittedList[index.toInt()]) }
That function will then be called to get numbers that will be compared to a list of expected numbers. It doesn't matter if there are additional numbers coming from scanForNumbers that aren't in the "target" list. They will just be ignored. Something like this:
val expectedNumbers = listOf("202", "302","999")
scanForNumbers(expectedNumbers)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe { value -> Log.d(TAG, "Was returned a $value") }
So, the expected numbers (202, 302, and 999) don't exactly match with the numbers that will be emitted (202, 302, and 400). So, a timeout SHOULD occur, but with the built in version of Observable.timer(), it will not time out since at least one item was observed.
Here is kind of what I'd like to have. Anyone know how to code this up in RxJava/RxKotlin?
fun scanForNumbers(targets: List<String>): Observable<FoundNumber> {
val accumulator: Pair<Set<Any>, FoundNumber?> = targets.toSet() to null
return scanNumbers()
.SPECIAL_TIMEOUT_FOR_LIST(5, TimeUnit.SECONDS, List)
.scan(accumulator) { acc, next ->
val (set, previous) = acc
val stringSet:MutableSet<String> = hashSetOf()
set.forEach { stringSet.add(it.toString()) }
val item = if (next.numberId in stringSet) {
next
} else null
(set - next) to item // return set and nullable item
}
.filter { Log.d(TAG, "Filtering on ${it.second}")
it.second != null } // item not null
.take(targets.size.toLong()) // limit to the number of items
.map { it.second } // unwrap the item from the pair
.map { FoundController(it.numberId) } // wrap in your class
}
How do you code, hopefully using RxJava/Kotlin, a means to timeout on a list as mentioned?
I think I get it now, you want the timeout to begin counting from the moment you subscribe, not after you observe items.
If this is what you need, then the takeUntil operator could help you:
return scanNumbers()
.takeUntil(Observable.timer(5, TimeUnit.SECONDS))
.scan(accumulator) { acc, next -> ...
In this case, the timer will begin counting as soon as you subscribe. If the main observable completes before then great, if not, then the timer will complete the main observable anyways.
But takeUntil by itself will not throw an error, it will just complete. If you need it to end with an error, then you could use the following combination:
return scanNumbers()
.takeUntil(
Observable
.error<Void>(new TimeoutError("timeout!"))
.delay(5, TimeUnit.SECONDS, true))
.scan(accumulator) { acc, next -> ...

removing elements from a sequence using for/yield

Given a Future[Seq[Widget]], where Widget contains a amount : Int property, I'd like to return a Seq[Widget] but for only those Widgets whose amount value is greater than 100. I believe the for { if … } yield { } construct will give me what I want but am unsure how to filter through the Sequence. I have:
val myWidgetFuture : Future[Seq[Widget]] = ...
for {
widgetSeq <- myWidgetFuture
if (??? amount > 100) <— what to put here?
} yield {
widgetSeq
}
If there's a clean non-yield way of doing this that will also work for me.
You don't even need yield. Use map.
val myWidgetFuture: Future[Seq[Widget]] = ???
myWidgetFuture map { ws => ws filter (_.amount > 100) }
If you want to use for … yield with an if filter, you'll need to use two fors:
for {
widgetSeq <- myWidgetFuture
} yield for {
widget <- widgetSeq
if widget.amount > 100
} yield widget

Getting scala's template loop index in playframework

I am trying to iterate in a playframework view, but without success for now. I have the following structure:
#if(list != null) {
for(a <- 0 to list.size()/5)
{
// some html, where I want to get the value of a
for(b <- a*5 to a*5+5) // here I want to use the a value again
{
some html
}
}
So my question is how to get the current index of the loop so that I will be able to use it.
You should combine it in one for loop:
#if(list != null) {
#for{a <- 0 to list.size()/5
b <- a*5 to a*5+5}
yield html
}
}
And use option instead of null checking.
Also you can use map function to transform your list. See details in Play documentation - http://www.playframework.com/documentation/2.0/ScalaTemplates