6

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.

Jack
  • 193
  • 3
  • 10
  • 1
    Do you mean to say you want to see if they are sorted in an ascending order? – userx Aug 02 '17 at 19:14
  • Just do a loop that check if the next element is the current element +1 – litelite Aug 02 '17 at 19:14
  • `array == array.sorted()`? – Sulthan Aug 02 '17 at 19:15
  • Or just check that for every two elements `a <= b` holds. – Sulthan Aug 02 '17 at 19:16
  • Possible duplicate of [Extending Array to check if it is sorted in Swift?](https://stackoverflow.com/questions/24602595/extending-array-to-check-if-it-is-sorted-in-swift) – Phyber Aug 02 '17 at 19:21
  • 1
    What *exactly* does "in sequence" mean? In increasing order or *consecutive* increasing integers? Does `[1, 3, 7]` count as "in sequence"? – Martin R Aug 02 '17 at 19:47
  • func inSequence(_ array1: [T]) -> Bool { return array1 == array1.sorted() } – Balagurubaran Aug 02 '17 at 20:38
  • @balagurubaran I believe, based on the OP:s statement regarding the "diff to be 1", that he does not want to ascertain whether the elements are _ordered_, but rather whether the are _consecutive_. Also, sorting an array just to see if it is ordered can be quite wasteful (verifying if an array is ordered is `O(n)`). – dfrib Aug 02 '17 at 20:42

4 Answers4

15

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()
Luca Angeletti
  • 58,465
  • 13
  • 121
  • 148
4

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)
    }
}
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
1

"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
    }
}
dfrib
  • 70,367
  • 12
  • 127
  • 192
0

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;
    }
Balagurubaran
  • 549
  • 6
  • 18
  • Your answer would be more helpful if you *explain* it as well. Btw, what does `isSeq([4,4,7,7],sequence:1)` return? – Martin R Aug 02 '17 at 22:05