Search code examples
arraysswiftsequences

Shortest way to find repetitive continuous values in an array


I've an array as

[1,5,3,3,4,4,4,5,6,6,6,6,8,9,1,1,5]

Here, '4' is continuously repeating 3 times and '6' is repeating 4 times. Array length is not fixed.

So I need to iterate this array to find the max number repeating continuously. So, 4 and 6 both are repeating but the highest is 6 so the function should return 6 and its starting index and ending index in short time.

I've a Case 2 as well.

[10,11,10,10,11,10,15,16,20,21,22,21,21,20,20]

In the above case, the slight difference between values can be negligible. For eg. first sequence has 10,11,10,10,11,10 and second sequence has 20,21,22,21,21,20,20 and the highest repetitive values will be second sequence

If tolerance is 1 then first case will be considered instead second sequence. Because if tolerance is 1 then the second sequence would be [21,21,20,20], hence maximum number(count) of occurrences is in first case.

Any help would be appreciated.


Solution

  • I made a reduce map for finding the max occurring result, I've added a enumerated map to the Int array for getting the value its index inside the reduce map, all code is tested with Swift 4.

    Sorting method:

    let array: [(Int, Int)] = [1, 5, 3, 3, 4, 4, 4, 5, 6, 6, 6, 6, 8, 9, 1, 1, 5].enumerated().map { ($0, $1) }
    
    var results: [Result] = []
    
    guard let maxResult = array.reduce(nil, { (maxResult, item) -> Result? in
        let (index, value) = item
        guard let result = results.first(where: { (result) -> Bool in
            result.value == value && result.endIndex + 1 == index
        }) else {
            let result = Result(value, startIndex: index)
            results.append(result)
            return result > maxResult ? result : maxResult
        }
        result.endIndex = index
        return result > maxResult ? result : maxResult
    }) else {
        print("no max result found :(")
        return
    }
    
    print("found max result: \(maxResult) in results: \(results)")
    

    Result class:

    class Result {
        let value: Int
        var numberOfOcurrences: Int
        let startIndex: Int
        var endIndex: Int {
            didSet {
                numberOfOcurrences += 1
            }
        }
    
        init(_ value: Int, startIndex: Int) {
            self.value = value
            self.startIndex = startIndex
            self.endIndex = startIndex
            self.numberOfOcurrences = 1
        }
    
        static func > (lhs: Result, rhs: Result?) -> Bool {
            return lhs.numberOfOcurrences > rhs?.numberOfOcurrences ?? 0
        }
    }
    
    extension Result: CustomStringConvertible {
    
        var description: String {
            return """
            Result(
                value: \(value),
                numberOfOcurrences: \(numberOfOcurrences),
                startIndex: \(startIndex),
                endIndex: \(endIndex)
            )
            """
        }
    }
    

    Output:

    /*  output: found max result: Result(
                value: 6,
                numberOfOcurrences: 4,
                startIndex: 8,
                endIndex: 11
            ) in results: [
                Result(
                    value: 1,
                    numberOfOcurrences: 1,
                    startIndex: 0,
                    endIndex: 0
                ),
                Result(
                    value: 5,
                    numberOfOcurrences: 1,
                    startIndex: 1,
                    endIndex: 1
                ),
                Result(
                    value: 3,
                    numberOfOcurrences: 2,
                    startIndex: 2,
                    endIndex: 3
                ),
                Result(
                    value: 4,
                    numberOfOcurrences: 3,
                    startIndex: 4,
                    endIndex: 6
                ),
                Result(
                    value: 5,
                    numberOfOcurrences: 1,
                    startIndex: 7,
                    endIndex: 7
                ),
                Result(
                    value: 6,
                    numberOfOcurrences: 4,
                    startIndex: 8,
                    endIndex: 11
                ),
                Result(
                    value: 8,
                    numberOfOcurrences: 1,
                    startIndex: 12,
                    endIndex: 12
                ),
                Result(
                    value: 9,
                    numberOfOcurrences: 1,
                    startIndex: 13,
                    endIndex: 13
                ),
                Result(
                    value: 1,
                    numberOfOcurrences: 2,
                    startIndex: 14,
                    endIndex: 15
                ),
                Result(
                    value: 5,
                    numberOfOcurrences: 1,
                    startIndex: 16,
                    endIndex: 16
                )
            ]
        */
    

    UPDATE: for case 2

    let array: [(Int, Int)] = [10, 11, 10, 10, 11, 10, 15, 16, 20, 21, 22, 21, 21, 20, 20].enumerated().map { ($0, $1) }
    
    var results: [Result] = []
    let tolerance: Int = 1 // now there can be one other in between
    
    guard let maxResult = array.reduce(nil, { (maxResult, item) -> Result? in
       let (index, value) = item
        guard let result = results.first(where: { (result) -> Bool in
            return result.value == value && result.endIndex + (1 + tolerance) >= index
        }) else {
            let result = Result(value, startIndex: index)
            results.append(result)
            return result > maxResult ? result : maxResult
        }
        result.endIndex = index
        return result > maxResult ? result : maxResult
    }) else {
        print("no max result found :(")
        return
    }
    
    print("found max result: \(maxResult) in results: \(results)")
    

    Output:

    /*  output: found max result: Result(
                value: 10,
                numberOfOcurrences: 4,
                startIndex: 0,
                endIndex: 5
            ) in results: [
                Result(
                    value: 10,
                    numberOfOcurrences: 4,
                    startIndex: 0,
                    endIndex: 5
                ), Result(
                    value: 11,
                    numberOfOcurrences: 1,
                    startIndex: 1,
                    endIndex: 1
                ), Result(
                    value: 11,
                    numberOfOcurrences: 1,
                    startIndex: 4,
                    endIndex: 4
                ), Result(
                    value: 15,
                    numberOfOcurrences: 1,
                    startIndex: 6,
                    endIndex: 6
                ), Result(
                    value: 16,
                    numberOfOcurrences: 1,
                    startIndex: 7,
                    endIndex: 7
                ), Result(
                    value: 20,
                    numberOfOcurrences: 1,
                    startIndex: 8,
                    endIndex: 8
                ), Result(
                    value: 21,
                    numberOfOcurrences: 3,
                    startIndex: 9,
                    endIndex: 12
                ), Result(
                    value: 22,
                    numberOfOcurrences: 1,
                    startIndex: 10,
                    endIndex: 10
                ), Result(
                    value: 20,
                    numberOfOcurrences: 2,
                    startIndex: 13,
                    endIndex: 14
                )
            ]
        */
    

    UPDATE 3: edited Result class to store number of values in sequence

    class Result {
        let value: Int
        var numberOfOcurrences: Int
        var numberOfValuesInSequence: Int
        let startIndex: Int
        var endIndex: Int {
            didSet {
                numberOfOcurrences += 1
                numberOfValuesInSequence = (endIndex - startIndex) + 1
            }
        }
    
        init(_ value: Int, startIndex: Int) {
            self.value = value
            self.startIndex = startIndex
            self.endIndex = startIndex
            self.numberOfOcurrences = 1
            self.numberOfValuesInSequence = 1
        }
    
        static func > (lhs: Result, rhs: Result?) -> Bool {
            return lhs.numberOfValuesInSequence > rhs?.numberOfValuesInSequence ?? 0
        }
    }
    
    extension Result: CustomStringConvertible {
    
        var description: String {
            return """
            Result(
                value: \(value),
                numberOfOcurrences: \(numberOfOcurrences),
                numberOfValuesInSequence: \(numberOfValuesInSequence),
                startIndex: \(startIndex),
                endIndex: \(endIndex)
            )
            """
        }
    }
    

    Output:

    Result(
        value: 10,
        numberOfOcurrences: 4,
        numberOfValuesInSequence: 6,
        startIndex: 0,
        endIndex: 5
    )