Is it possible to identify the last element of an array within a 'for' statement like the following?
for (ai in list) {
}
It's not a requirement, it's mainly curiosity.
You can with enough trickery (i.e. metaprogramming). The classes below define a custom iterator that dynamically injects a readonly last property into each element and a custom List implementation for returning the custom iterator.
import groovy.transform.TupleConstructor
#TupleConstructor
class LastAwareIterator<T> implements Iterator<T> {
Iterator itr
boolean hasNext() {
itr.hasNext()
}
void remove() {
itr.remove()
}
T next() {
T obj = itr.next()
boolean last = !itr.hasNext()
obj.metaClass.isLast << { -> last }
obj
}
}
class LastAwareList<T> extends ArrayList<T> {
Iterator<T> iterator() {
new LastAwareIterator(super.iterator())
}
}
When you coerce your list into LastAwareList using as, you can use the last property within the for loop body.
def list = [1,2,3,4,2] as LastAwareList
for (ai in list) {
if (ai.last) {
print 'and '
}
print "$ai "
}
// prints 1 2 3 4 and 2
I had originally wanted to use ExpandoMetaClass to directly override the iterator method, avoiding the need for as LastAwareList, but couldn't get it working. If I ever figure it out, I'll update the post.
A counter has to be maintained to check the last item in the list.
def printList(list) {
int x = 1
for ( ai in list ) {
if ( x == list.size() ) println "and"
println ai
x++
}
println ""
}
printList([1,2,3,4,5])
printList([1,2,3,4,2])
printList([2,2,2])
printList([])
printList(null)
Also try with list having duplicate elements and the last item being a duplicate.
I know it isn't what you asked, but I just want to bring to your attention that in groovy there is a really cool way of getting the last element.
def list = [1,2,3]
println list.last()
Try this
def list = [1,2,3,4,5,6] //your list
def last = list.last() //got last item of list
list.each { item ->
if (item == last) { //checking last item
println ("and "+ item)
} else {
println(item)
}
}
//result : 1 2 3 4 5 and 6
Related
As of right now i retrieve the first item in my list that i generate by using split, but i also want the last item in a seperate function.
The problem ist the list is generated from an ocr and is never the same length or size.
is there a way in kotlin to always get the last item?
This is the code for getting the first :
fun String.firstLine(): String {
if (this.isEmpty()) return ""
return this.split("\n").get(0)
}
You can do something like this:
fun String.firstAndLast() = split("\n").let { it.first() to it.last() }
fun main() {
val s = "first line\nsecond line\nlast line"
val (first, last) = s.firstAndLast()
println(first)
println(last)
}
output is:
first line
last line
The fun will split, then use the resulting list and get only the first and last element, returning a Pair<String, String>, which can be easily deconstructed with val (first, last), then those values can be used separately.
Edit: for the penultimate as well, I don't think that a list might be a good idea. I personally would go with an object of sorts, or with a Triple.
Class way:
class NoNameIdeas(
val first: String = "",
val penultimate: String = "",
val last: String = ""
) {
companion object {
fun fromString(string: String): NoNameIdeas {
val l = string.split("\n")
val first = l.first()
return when (l.size) {
1 -> NoNameIdeas(first = first)
2 -> NoNameIdeas(first = first, penultimate = "what should this be?", last = l.last())
else -> NoNameIdeas(first = first, penultimate = l[l.lastIndex - 1], last = l.last())
}
}
}
}
fun main() {
val string = "first line\nsecond line\nsecond last\nlast line"
val result = NoNameIdeas.fromString(string)
println(result.first)
println(result.penultimate)
println(result.last)
}
As you can guess, I have no idea how to name this object. Another issues is, what should we do with the penultimate, if we only have 2 lines in total? It can be the same as the first, or it can be empty, or it can be the same as the last. That is your decision, it also might never happen, only you know what data you have.
Another way of doing it without a class, but by using a triple:
Triple way:
fun main() {
val string = "first line\nsecond line\nsecond last\nlast line"
val result = string.firstPenultimateAndLast()
println(result.first)
println(result.second)
println(result.third)
}
fun String.firstPenultimateAndLast(): Triple<String, String, String> {
val l = split("\n")
val first = l.first()
var penultimate = "" //same as the class, what happens when we only have 2 lines?
var last = l.first() //if we only have 1, then I guess that the first is the last as well, will get a new value otherwise
when (l.size) {
2 -> last = l.last()
else -> {
penultimate = l[lastIndex - 1]
last = l.last()
}
}
return Triple(first, penultimate, last)
}
Func to see if two arrays have some same values:
func hasAllSame(largeCombinedArry:[Int], wantToKeep: [Int])->Bool{
var howManySame = [Int]()
for intElement in largeCombinedArry{
if wantToKeep.contains(intElement){
howManySame.append(intElement)
}
}
howManySame.sort()
if wantToKeep == howManySame{
print("These are the same!")
return true
}
else{
print("These are NOT same!")
return false
}
}
Array of tuples declared like this:
var TuplesArry:[(score: Double, value: [Int])] = []
Array filled thusly:
for (arry1, score) in zip(arryOfArrays, AllScores) {
let calculatedDiff = otherValue - score
TuplesArry.append((score: calculatedDiff, value: arry1))
}
var arrayForComparison = [8,9,7,6]
arrayForComparison.sort()
Error occurs here after several iteration at function call hasAllSame()
for i in 0..<TuplesArry.count{
if hasAllSame(largeCombinedArry: TuplesArry[i].value, wantToKeep:
arrayForComparison){
//do nothing
}
else{
/*
want to remove tuple that does not have all elements that are
in arrayForComparison
*/
TuplesArry.remove(at: i)
}
}
This code seems to be working but it seems like tuplesArry.count continues to decrease and iterator i continues to increase until error occurs "fatal index out of range"
My goal is to remove a tuple from the array of tuples if it's value does not meet criteria.
I've also tried something like:
for tuple in TuplesArry{
if hasAllSame(largeCombinedArry: tuple.value, wantToKeep:
arrayForComparison){
//do nothing
}
else{
//this does NOT work
let index = TuplesArry.index(of:tuple)
TuplesArry.remove(at: index)
}
}
The immediate issue is that you need to iterate in reverse to avoid the "index out of range" issue.
The much simpler solution is to use filter on your array. Then you entire for loop can be replaced with:
TouplesArry = TouplesArry.filter { hasAllSame(largeCombinedArry: $0.value, wantToKeep: arrayForComparison) }
I have index out of range error when i delete object from my array. There is my code. It is an Elevator function which takes object of Floor class and works with array of objects of Passenger class on this floor. I create a temporary copy of object of current floor and then I go along array of this copy and if object is suitable for the conditions we push this object to Elevator array of passengers and delete it by index from original array of current floor object. If this matters, I use the Equatable protocol and created a function to compare.
Thanks for any answers.
class Passenger: Equatable{...}
func ==(l: Passenger, r: Passenger) -> Bool {
return l === r
}
func checkFloor(f: Floor){
var tempFloor = f
var pass = passengers
for i in 0..<passengers.count {
if(passengers.isEmpty){
break
}
if(pass[i].getFloor()==f.getIdFloor()){
f.peoples.append(pass[i])
f.peoples[f.peoples.count - 1].setDirection(who: "nothing")
//if var index = passengers.index(of: pass[i]) {
if let index = searchInArray(who: passengers, who: pass[i]) {
passengers.remove(at: index)
}
}
}
// in this part I have a problem
for i in 0..<tempFloor.countOf() {
if(f.peoples.isEmpty || passengers.count >= capacity){
break
}
if(tempFloor.peoples[i].getDirection()==tempFloor.peoplesDirection()
){
passengers.append(tempFloor.peoples[i])
if let index = f.peoples.index(of: tempFloor.peoples[i]) {
if (index >= 0 && index < f.peoples.count) {
//print(index)
f.peoples.remove(at: index) // index out of range error
}
}
}
}
}
You are removing items whilst enumerating a range, so the range is changing (potentially often) but this wont update for i in 0..<tempFloor.countOf()
When you remove an item from an array, each item after that index changes its index and the count is reduced. so if you plan to do this, it's usually best to enumerate the array backwards, so the removal of the current item will not affect what your doing next.
To demonstrate, try this code in a playground
var arr = [1,2,3,4,5,6,7,8,9,10]
for (index, item) in arr.enumerated().reversed() {
if item % 2 == 0 {
arr.remove(at: index)
}
}
print(arr)
It will iterate over the items in the array backwards and remove any that are even, and will output:
"[1, 3, 5, 7, 9]\n"
Im wondering if it is possible to insert an element at index 1 but not index 0 in swift like this:
var array = [String]()
array.insert("cow", atIndex: 1)
but every time I try I get the old fatal error: Array index out of range error message.
Is there anyway around this problem? Any suggestions would be greatly appreciated! Thanks!
If you make it an array of optionals and initialize the number of elements you want first, you can get close.
var array = [String?]()
for i in 0...5 {
array.append(nil)
}
array.insert("cow", atIndex: 1)
If you really want the index to be specific, rather than just the next available position in the array, you should use a dictionary with Int keys.
var dict = [Int:String]()
dict[1] = "Cow"
dict[5] = "Chicken"
You can create a custom list. You will need to add some checking to make sure items aren't null or out of index, etc.
void Main()
{
var list = new CustomList<string>();
list.Add("Chicken");
list.Add("Bear");
list[1] = "Cow";
list[1].Dump(); //output Cow
}
public class CustomList<T>
{
IList<T> list = new List<T>();
public void Add(T item)
{
list.Add(item);
}
public T this[int index]
{
get
{
return list[index - 1];
}
set
{
list[index - 1] = value;
}
}
}
Literally you can't.
An item can be inserted up to the maximum index index(max) = array.count, in case of an empty array at index 0.
I'm trying to create a function in Xcode that I can call every time I hit a button to iterate through an array in sequence which then updates the value of the button title.
I can't seem to crack the challenge. I've tried various iterations of while loops and if statements but everytime I run it I end straight up at the last value in the array. Here's the code I've got at the moment, I tried to add a break clause to stop the function from automatically iterating through the whole array but it's now throwing up an error message saying that the code after the return statement will never be executed:
So, I've created an instance of a button within my viewController as follows:
#IBAction func repCount() {
repCountButton.setTitle("\(repCounter.repCount())", forState: UIControlState.Normal)
I'm hoping that this will then update the title of the button with what I return from the repCount function that is called every time the button is pressed.
I've set up the function in a separate Swift file called repCounter and my code for the repCount function is as follows:
var repArray = [1,2,3,4,5]
var repArrayIndex: Int = 0
func repCount () -> String {
if repArrayIndex < repArray.count {
while repArrayIndex < repArray.count {
return "\(repArray[repArrayIndex])"
break
}
repArrayIndex++
} else {
return "\(repArray[0])"
}
}
What I'd like this to do is to cycle through the array every time it is called and once it's got to the end of the array to start cycling from the beginning of the array again.
Thanks in advance for any help!
I'm not at a computer where I can pull up XCode to test it, but I think the version below will do what you want. It isn't the most elegant code, but it is very straightforward. You have to do all the index juggling before the return statement since once the code hits a return, nothing following it will be executed.
I added some code to reset the index once it reaches the end of the array.
var repArray = [1,2,3,4,5]
var repArrayIndex: Int = 0
func repCount () -> String {
while repArrayIndex < repArray.count {
var curIndex = repArrayIndex
repArrayIndex = repArrayIndex + 1;
if repArrayIndex >= repArray.count {
repArrayIndex = 0
}
return "\(repArray[curIndex])"
}
return "\(repArray[0])"
}
Another option to getting this count, without iterating, is to do
// From Swift 1.2
func repCount () -> String {
return count(repArray)
}
// Before Swift 1.2
func repCount () -> String {
return countElements(repArray)
}
If you insist on iterating there are multiple options, see Iterating over an array, one which could be:
var count = 0
for rep in repArray {
count++
}
Or you could for the round trip iteration provided in the other answer, but why do it the hard way, when you don't need to? Or is there something you haven't told us?