Is there any best method to check if elements in array are in consecutive order?
Eg:
[1,2,3,4,5] // returns true
[1,2,4,3,5] // returns false
Currently what I implement is to take difference of elements and if the diff is 1 then I say it is in consecutive order.
I'm looking for any improved approach. I think of adding extension to Array but not sure how to implement this.
Given your array
let list = [1,2,3,4,5]
you can use some Functional Programming magic
let consecutives = list.map { $0 - 1 }.dropFirst() == list.dropLast()
If this is a one-off question, then any little for-loop is fine, but it's an interesting problem to explore generic solutions. First, I'm assuming you mean that each element must be one greater than the one before, not just in order.
Let's build a generic way to answer "do all pairs of elements in this collection obey some rule." First, it'd be really nice to have a generic way to say "do all?"
extension Sequence {
func all(pass predicate: (Element) -> Bool) -> Bool {
// If nothing is false, everything is true
return !self.contains(where: { !predicate($0) })
}
}
This returns whether all elements of a sequence obey some rule.
Now we can ask the question: do all pair-wise elements of a collection obey some rule:
extension Collection {
func passesForConsecutiveValues(_ predicate:(Element, Element) -> Bool) -> Bool {
return zip(self, dropFirst()).all(pass: predicate)
}
}
zip(x, x.dropFirst() just creates "pair-wise elements", and then we ask "do they all satisfy our rule?" For example:
// Are all elements one more than their predecessor?
[1,2,4,5].passesForConsecutiveValues { $1 == $0 + 1 } // true
Now you may have noticed that I switched from Sequence to Collection in the middle there. Why? Because zip(x, x.dropFirst()) isn't defined on arbitrary sequences. You may only be allowed to iterate over a sequence once. Unfortunately there's no way to know; it's considered "special knowledge about the sequence" in the docs. Bleh. I miss Scala's TraversableOnce vs. Sequence that moves the requirement into the type.
That said, we absolutely can build this for Sequence. We just have to build a replacement for zip(x, x.dropFirst()). We'll call it pairwise and it'll return an iterator:
extension Sequence {
func pairwise() -> AnyIterator<(Element, Element)> {
var it = makeIterator()
guard var last_value = it.next() else { return AnyIterator{ return nil } }
return AnyIterator {
guard let value = it.next() else { return nil }
defer { last_value = value }
return (last_value, value)
}
}
}
And with that, we can build this on Sequence:
extension Sequence {
func passesForConsecutiveValues(_ predicate:(Element, Element) -> Bool) -> Bool {
return pairwise().all(pass: predicate)
}
}
"Currently what I implement is to take difference of elements and if
the diff is 1 then I say it is in sequence."
Based on your statement above, it seems your want to, for an array of integers, see if all members are consecutive.
You've already described the logic for this algorithm: you could implement it e.g. using a for ... in ... where loop, with a body which is only ever entered when the where clause identifies two subsequent elements which are not in consecutive order. E.g.:
extension Array where Element == Int {
func numbersAreConsecutive() -> Bool {
for (num, nextNum) in zip(self, dropFirst())
where (nextNum - num) != 1 { return false }
return true
}
}
var arr = [1, 2, 3, 4, 5]
print(arr.numbersAreConsecutive()) // true
arr = [1, 2, 4, 5]
print(arr.numbersAreConsecutive()) // false
arr = [1]
print(arr.numbersAreConsecutive()) // true
arr = []
print(arr.numbersAreConsecutive()) // true
arr = [2, 1]
print(arr.numbersAreConsecutive()) // false
Extending the extension to all types conforming to Integer:
extension Array where Element: Integer {
func numbersAreConsecutive() -> Bool {
for (num, nextNum) in zip(self, dropFirst())
where (nextNum - num) != 1 { return false }
return true
}
}
It will return the true if the sequence is expected otherwise it will return the false
It has two check
1.Checking whether the array is sequence(Find the array is sequence)
1.1 Sortedarray[0] + arraycount multiple with sequence (1,2,3, etc) and minus the sequence.
1.2 compare the above calculated value with last value of sorted array. if it matche we could consider The array is sequence.
2. Compare the source array and sorted array to confirm it is in order
isSeq([4,5,6,7],sequence:1) **return True**
isSeq([100,102,104,106,108],sequence:2) **return True**
isSeq([100,103,106,109,110],sequence:3) **return false**
func isSeq(_ arrayValue:[Int],sequence:Int) ->Bool{
let sortedValue = arrayValue.sorted()
if(sortedValue[0] + (sortedValue.count * sequence) - sequence == sortedValue[sortedValue.count - 1]){
if(arrayValue == sortedValue){
return true
}
}
return false;
}
Related
This is a follow up on this thread, which main issue was to iterate over all permutations of an array, that is given ["a","b","c"], obtain ["bca","acb".. etc], using an iterator.
Thanks to Martin R's insights, and also his inputs in another thread, I came up with another possible solution for the 'Sequence-Based enumeration of permutations' problem using iterators. The issue is that I'm not sure I have all permutations although there are good indications they're all there. The algorithm is guaranteed to provide n! permutations at the most, with no duplicates.
The idea behind this approach is the following, say there's an array a=["a","b","c"...], of size n. Listing all permutations can be viewed as picking elements from bags:
■
■
■
■ ■
■ ■ ■
■ ... ■ ■ ■
0 ... n-3 n-2 n-1
So the algorithm takes the initial array, and removes a row, pass it recursively until there is no row left. At this point, if an iterator can be found, all individual permutations can be addressed independently. The iterator is hidden in FactorialSequence below, where a method next() allows to move from adjacents points.
public struct FactorialSequence : Sequence, IteratorProtocol {
private var current: [Int]
public init(size:Int) {
self.current = Array(repeating: 0, count: size)
}
public mutating func next() -> [Int]? {
return self.__next();
}
private mutating func __next() -> [Int]? {
var next = current
defer {
current = next;
}
for i in self.current.indices.reversed() {
if next[i] < current.count - i - 1 {
next[i] += 1
return next;
}
next[i] = 0
}
return nil
}
}
func permute(seq:[String],at:[Int]) -> String? {
if seq.count > 0 {
var ss = seq;
let uu = seq[at.first!]
var cc = at;
_ = ss.remove(at: cc.first!)
_ = cc.remove(at: 0);
return uu + (permute(seq:ss,at:cc) ?? "")
}
return nil ;
}
the permute() function is called passing the iterator (an array) calculated from FactorialSequence:
var fs = FactorialSequence(size: 3)
print("\(fs.current):\(permute(seq:["a","b","c"], at: fs.current)!)")
while let uu = fs.next() {
print("\(uu):\(permute(seq:["a","b","c"], at: uu)!)")
}
and gives (in flatten string format):
[-0.000][-0.000][171] [0, 0, 0]:abc
[0.0009][0.0009][174] [0, 1, 0]:acb
[0.0016][0.0007][174] [1, 0, 0]:bac
[0.0024][0.0008][174] [1, 1, 0]:bca
[0.0032][0.0008][174] [2, 0, 0]:cab
[0.0040][0.0008][174] [2, 1, 0]:cba
Note on 'no duplicates': Since permutations are accessed using an array (the iterator), if two iterators differ by one elements, they point to two different permutations. Although a little thin, I take this as an argument for not having duplicates.
The only question remaining is 'are they all there?'. One could say that there are n! distinct arrays pointing at a given permutation, but I'm not too sure about the validity of that argument, since it comes from a 'drawing'... Pointers welcome.
I didn't thoroughly scrub SO to check if this had been already formulated this way or in a similar way (although links in the original thread use other approaches). Apologies if it did.
For a given size N the FactorialSequence produces a sequence of all arrays
[ i.0, i.1, ..., i.(N-1) ]
such that
0 <= i.0 < N, 0 <= i.1 < N-1, ..., 0 <= i.(N-1) < 1
that are exactly
N * (N-1) * ... * 1 = N!
elements. The permute() function then picks the element with index i.0
from the given array with N elements, then the element with i.1 from
the remaining N-1 elements, and so on.
So yes, this indeed produces all possible permutations of the array.
However, the code can be simplified a bit. First, FactorialSequence
does not return the initial array [ 0, ..., 0 ], corresponding to
the identity permutation. Also the separate __next() method seems
unnecessary.
If we change the code to
public struct FactorialSequence : Sequence, IteratorProtocol {
private var current: [Int]
private var firstIteration = true
public init(size:Int) {
self.current = Array(repeating: 0, count: size)
}
public mutating func next() -> [Int]? {
if firstIteration {
firstIteration = false
return current
}
for i in self.current.indices.reversed() {
if current[i] < current.count - i - 1 {
current[i] += 1
return current;
}
current[i] = 0
}
return nil
}
}
then all permutations are returned (including the initial identity), and
the defer statement is no longer necessary.
The permute() function can be simplified slightly, and made generic:
func permute<E>(array: [E], at: [Int]) -> [E] {
if at.isEmpty { return [] }
var at = at
var array = array
let firstIndex = at.remove(at: 0)
let firstElement = array.remove(at: firstIndex)
return [firstElement] + permute(array: array, at: at)
}
Now
let array = ["a", "b", "c"]
let fs = FactorialSequence(size: 3)
for p in fs {
print(permute(array: array, at: p).joined())
}
produces the expected output.
Note however that permute() produces a lot of intermediate arrays,
therefore I assume it to be less efficient than the other methods
that you referenced.
An alternative would be to swap the picked element to its new
place, this avoids recursion and temporary arrays:
func permute<E>(array: [E], at: [Int]) -> [E] {
var result = array
for (i, j) in at.enumerated() {
result.swapAt(i, i + j)
}
return result
}
(It produces the permutations in a different order, though.)
This is the code for sorting an array by the each cells in the tableView's timestamp.
self.ProjectsArray.sorted(by: { (project, project2) -> Bool in
return project.timestamp?.intValue < project2.timestamp?.intValue
})
Is there a better way to sort an array? What am I'm doing wrong?
EDIT- According to your comments you want to sort in place, so I am updating to sort in place.
Your timestamp variable is an Optional, so you may be comparing nil to nil, or nil to an Int. You can either unwrap these safely and provide a sort order in the case that one is nil, or you can use a nil-coalescing operator to treat nil value as some default Int like 0. The two options look like this:
Optional unwrapping:
self.ProjectsArray.sort(by: { (project, project2) -> Bool in
if let timestamp1 = project.timestamp, let timestamp2 = project2.timestamp {
return timestamp1.intValue < timestamp2.intValue
} else {
//At least one of your timestamps is nil. You have to decide how to sort here.
return true
}
})
Nil-coalescing operators:
self.ProjectsArray.sort(by: { (project, project2) -> Bool in
//Treat nil values as 0s and sort accordingly
return (project.timestamp?.intValue ?? 0) < (project2.timestamp?.intValue ?? 0)
})
I am given a list of strings and I need to see if they are in alphabetical order.
I know I need to use a for loop and check the first character of each string but I don't know how to progress from there.
for (item in array)
println(item[0])
For example ["adam", "ben", "chloe"] should return true.
And likewise for ["ben", "adam", "chloe"] should return false.
UPD: Since Kotlin 1.2 the following is available:
Most efficient variant, creates the smallest amount of intermediate objects:
listOf(1, 2, 3).asSequence().zipWithNext { a, b -> a <= b }.all { it }
Note on efficiency: Creates only two Sequence objects and one function object. Uses Boolean constants as an intermediate value. The final all invocation is inlined as a while loop over the sequence.
Slightly less efficient variant:
listOf(1, 2, 3).asSequence().windowed(2).all { (a, b) -> a <= b }
It's slightly less efficient, as it creates creates intermediate List(a, b) for every element of the original list.
Both of these variants were listed in the answers below by #Billbucket and #AshishChaudhary.
Old answer, for previous versions of Kotlin:
Here is a one liner:
val a = listOf("a", "b", "c")
a.zip(a.drop(1)).all { (a, b) -> a <= b }
// true
Explanation:
a.zip(a.drop(1)) returns pairs of neighbour elements. If in every pair first element is less or equal to the next, array is in order.
If you want to improve performance, you can wrap your array in lazy sequence first. In this case array won't be copied:
a.asSequence().let { it.zip(it.drop(1)).all { (a, b) -> a < b } }
The whole thing is O(N) (single pass through array), which is optimal for this task.
you can use a simple one-liner:
array.zipWithNext { s1, s2 -> s1 <= s2 }.all { it }
Im sure you could do your desired task completely using a for-loop.
However in Kotlin I personally think it would be more idiomatic to do something like this using until:
fun isAlphabetical(stringsArray: Array<String>): Boolean {
if (stringsArray.size < 2) return true
return (1 until stringsArray.size).none { stringsArray[it] <= stringsArray[it - 1] }
}
fun main(args: Array<String>) {
val testCases = arrayOf(arrayOf("adam", "ben", "chloe"), arrayOf("ben", "adam", "chloe"))
for(testCase : Array<String> in testCases){
println("The strings are: ${testCase.joinToString()}")
if (isAlphabetical(testCase)) {
println("Strings are in alphabetical order.\n")
} else {
println("Strings are not in alphabetical order.\n")
}
}
}
Output:
The strings are: adam, ben, chloe
Strings are in alphabetical order.
The strings are: ben, adam, chloe
Strings are not in alphabetical order.
Basically you only compare each element of the array with the previous element (using <=) if the length of the array is more than 1.
A very simple and easy way to accomplish it is by sorting and comparing the original list with the sorted one (two lists are equal when they have the exact same elements in the same order). Since you mentioned you are dealing with an array, you first need to convert it to a list. This solution is not the best in terms of performance (it's O(n log n) and converts the array twice to a list) but it's very readable:
val test = array.asList() == array.asList().sorted()
The full code for your question could be:
println(if (array.asList() == array.asList().sorted()) "Strings are in alphabetical order." else "Strings are not in alphabetical order.")
Another solution:
val list = listOf("a", "b", "c")
list.windowed(2).none { (a, b) -> a > b }
// true
In case you want to compare arbitrary Comparable list:
fun <T : Comparable<T>> isCollectionSortedAsc(list: Collection<T>): Boolean {
return list.zipWithNext { a, b -> a.compareTo(b) == -1 }.all { it }
}
Based on the accepted answer above: https://stackoverflow.com/a/47296632/4919972
Yet another solution :)
data class Result(val isInOrder: Boolean, val lastString: String) {
val toString = when {
isInOrder -> "Strings are in alphabetical order."
else -> "Strings are not in alphabetical order."
}
}
fun Array<String>.isInAlphabeticalOrder() =
this.fold(Result(true, ""), { acc, word -> Result(acc.isInOrder && acc.lastString < word, word) })
fun main(args: Array<String>) {
val test1 = arrayOf("adam", "ben", "chloe")
val test2 = arrayOf("ben", "adam", "chloe")
println(test1.isInAlphabeticalOrder().toString)
println(test2.isInAlphabeticalOrder().toString)
}
I have the following code in Swift 3:
var numbers = [1,2,1]
for number in numbers.count - 1 { // error
if numbers[number] < numbers[number + 1] {
print(number)
}
}
I am checking if the value on the index [number] is always higher than the value on the index [number + 1]. I am getting an error:
Type Int does not conform to protocol sequence
Any idea?
It may be swift.
You can use this iteration.
for number in 0..<(numbers.count-1)
The error is because Int is not a Sequence. You can create a range as already suggested, which does conform to a sequence and will allow iteration using for in.
One way to make Int conform to a sequence is:
extension Int: Sequence {
public func makeIterator() -> CountableRange<Int>.Iterator {
return (0..<self).makeIterator()
}
}
Which would then allow using it as a sequence with for in.
for i in 5 {
print(i)
}
but I wouldn't recommend doing this. It's only to demonstrate the power of protocols but would probably be confusing in an actual codebase.
From you example, it looks like you are trying to compare consecutive elements of the collection. A custom iterator can do just that while keeping the code fairly readable:
public struct ConsecutiveSequence<T: IteratorProtocol>: IteratorProtocol, Sequence {
private var base: T
private var index: Int
private var previous: T.Element?
init(_ base: T) {
self.base = base
self.index = 0
}
public typealias Element = (T.Element, T.Element)
public mutating func next() -> Element? {
guard let first = previous ?? base.next(), let second = base.next() else {
return nil
}
previous = second
return (first, second)
}
}
extension Sequence {
public func makeConsecutiveIterator() -> ConsecutiveSequence<Self.Iterator> {
return ConsecutiveSequence(self.makeIterator())
}
}
which can be used as:
for (x, y) in [1,2,3,4].makeConsecutiveIterator() {
if (x < y) {
print(x)
}
}
In the above example, the iterator will go over the following pairs:
(1, 2)
(2, 3)
(3, 4)
This maybe a little late but you could have done:
for number in numbers { }
instead of:
for number in numbers.count - 1 { }
For a for loop to work a sequence (range) is needed. A sequence consists of a stating a value, an ending value and everything in between. This means that a for loop can be told to loop through a range with ether
for number in 0...numbers.count-1 { } `or` for number in numbers { }
Both example give the nesasery sequences. Where as:
for number in numbers.count - 1 { }
Only gives one value that could either be the starting or the ending value, making it impossible to work out how many time the for loop will have to run.
For more information see Apple's swift control flow documnetation
This error can also come about if you try to enumerate an array instead of the enumerated array. For example:
for (index, element) in [0, 3, 4] {
}
Should be:
for (index, element) in [0, 3, 4].enumerated() {
}
So first you need to understand what is sequence..
A type that provides sequential, iterated access to its elements.
A sequence is a list of values that you can step through one at a time. The most common way to iterate over the elements of a sequence is to use a for-in loop:
let oneTwoThree = 1...3. // Sequence
for loop actually means
For number in Sequences {}
So you need to use
for number in 0..<(numbers.count-1) {}
The error is because number is not an index, but the element of the array on each iteration. You can modify your code like this:
var numbers = [1,2,1,0,3]
for number in 0..<numbers.count - 1 {
if numbers[number] < numbers[number + 1] {
print(numbers[number])
}
}
Or there is a trick using the sort method, but that's kind of a hack (and yes, the subindexes are right, but look like inverted; you can try this directly on a Playground):
var numbers = [1,2,1,0,3]
numbers.sort {
if $0.1 < $0.0 {
print ($0.1)
}
return false
}
For me, this error occurred when I tried writing a for loop, not for an array but a single element of the array.
For example:
let array = [1,2,3,4]
let item = array[0]
for its in item
{
print(its)
}
This gives an error like: Type Int does not conform to protocol 'sequence'
So, if you get this error in for loop, please check whether you are looping an array or not.
So say I have an array:
var stringArray = ["a","b","c","d","e","f","g","h","i","j"]
Now, how do I delete "a", "c", "e", "g", and "i" (all the even number indexes from the array)?
Thanks!
Instead of using C-style for-loops (which are set to be deprecated in an upcoming version of Swift), you could accomplish this using strides:
var result = [String]()
for i in stride(from: 1, through: stringArray.count - 1, by: 2) {
result.append(stringArray[i])
}
Or for an even more functional solution,
let result = stride(from: 1, to: stringArray.count - 1, by: 2).map { stringArray[$0] }
Traditional
var filteredArray = []
for var i = 1; i < stringArray.count; i = i + 2 {
filteredArray.append(stringArray[i])
}
Functional alternative
var result = stringArray.enumerate().filter({ index, _ in
index % 2 != 0
}).map { $0.1 }
enumerate takes a array of elements and returns an array of tuples where each tuple is an index-array pair (e.g. (.0 3, .1 "d")). We then remove the elements that are odd using the modulus operator. Finally, we convert the tuple array back to a normal array using map. HTH
There are a bunch of different ways to accomplish this, but here are a couple that I found interesting:
Using flatMap() on indices:
let result: [String] = stringArray.indices.flatMap {
if $0 % 2 != 0 { return stringArray[$0] }
else { return nil }
}
Note: result needs to be defined as a [String] otherwise the compiler doesn't know which version of flatMap() to use.
Or, if you want to modify the original array in place:
stringArray.indices.reverse().forEach {
if $0 % 2 == 0 { stringArray.removeAtIndex($0) }
}
In this case you have to call reverse() on indices first so that they're enumerated in reverse order. Otherwise by the time you get to the end of the array you'll be attempting to remove an index that doesn't exist anymore.
Swift 4.2
A function accepting generics and producing reduced result
func stripElements<T>(in array:[T]) -> [T] {
return array.enumerated().filter { (arg0) -> Bool in
let (offset, _) = arg0
return offset % 2 != 0
}.map { $0.element }
}