Replacing The Last Specific Character in a String

Thanks, no I'm fine, as I mentioned, it turned out to be the application of the regex specifically within the KM action that had thrown me (initially I was puzzled why the end of the string didn't seem to be explicitly matched - the answer was that within this context, it didn't have to be).

1 Like

I think this was the point that @mrpasini was making earlier -- it's generally true (ie not just in KM) that search and replace regex operations find a pattern then replace within that found part of the string, leaving the rest untouched. For example:

So another way of doing what the OP wanted (assuming the timestamp was at the end of a line) would be:

...and you can see that you don't care about what's before that final : at all!

3 Likes

Right, yes. It's been useful to find out that my regex habits have lead to unneeded complexity for "search and replace" operations. It also started me reflecting more generally on some of the ways in which depth of knowledge and breadth of experience can interact when tackling problems.

3 Likes

Late to the party. FWIW, a Swift solution:

Swift test.kmmacros (5.7 KB)

Expand disclosure traingle to view Swift source
import Foundation

// main :: IO ()
func main() -> () {
    let str = ProcessInfo.processInfo.environment["KMVAR_localParameter"]!
    
    return print(
        (concat
         • (+)
         • bimap(intersperse(":"))(cons("."))
         • splitAt(3)
         • splitOn(":")
        )(str)
    )
}

// FUNCTIONS --
// https://github.com/unlocked2412/Swift-Prelude
// Swift Precedence & Operators --------------------------------
precedencegroup ApplicativePrecedence {
    associativity: left
    higherThan: BitwiseShiftPrecedence
}

infix operator <*> : ApplicativePrecedence

precedencegroup CompositionPrecedence {
    associativity: right
    
    // This is a higher precedence than the exponentiative operators `<<` and `>>`.
    higherThan: BitwiseShiftPrecedence, ApplicativePrecedence
}

// Swift Prelude -----------------------------------------------
// apFn :: (a -> b -> c) -> (a -> b) -> (a -> c)
func <*><A, B, C>(_ f: @escaping (A) -> (B) -> C, _ g: @escaping (A) -> B) -> ((A) -> C) {
    return { x in f(x)(g(x)) }
}

// bimap :: (a -> b) -> (c -> d) -> (a, c) -> (b, d)
func bimap<A, B, C, D>(_ f: @escaping (A) -> B) -> (@escaping (C) -> D) -> (A, C) -> (B, D) {
    {
        g in {
            (a, c) in (f(a), g(c))
        }
    }
}

// concat :: [[a]] -> [a]
func concat<A: RangeReplaceableCollection>(_ xs: [A]) -> A {
    return .init(xs.flatMap { $0 } )
}

// cons :: a -> [a] -> [a]
func cons<A>(_ x: A) -> ([A]) -> [A] {
    // If the second argument is a list of Character,
    // the first argument get inferred as a single Character.
    return { xs in [x] + xs }
}

// drop :: Int -> Sequence a -> [a]
func drop<A, S: Sequence>(_ n: Int) -> (S) -> [A] where S.Iterator.Element == A {
    return { xs in Array(xs.dropFirst(n))}
}

// head :: [a] -> a
func head<A>(_ xs: [A]) -> A {
    return xs.first!
}

// intersperse :: a -> [a] -> [a]
func intersperse<A: Equatable>(_ sep: A) -> ([A]) -> [A] {
    {xs in
        {
            switch xs {
            case []:
                return []
            default:
                let x = head(xs)
                let xs1 = tail(xs)
                return cons(
                    x
                )(
                    prependToAll(sep)(xs1)
                )
            }
        }()
    }
}

// prependToAll :: a -> [a] -> [a]
func prependToAll<A: Equatable>(_ sep: A) -> ([A]) -> [A] {
    {xs in
        {
            switch xs {
            case []:
                return []
            default:
                let x = head(xs)
                let xs1 = tail(xs)
                return cons(
                    sep
                )(
                    cons(x)(
                        prependToAll(sep)(xs1)
                    )
                )
            }
        }()
    }
}

// splitAt :: Int -> [a] -> ([a],[a])
func splitAt<S: Sequence>(_ n: Int) -> (S) -> ([S.Element], [S.Element]) {
    return {xs in (take(n)(xs), drop(n)(xs)) }
}

@available(macOS 13.0, *)
// String.Element ?
// splitOn :: String -> String -> [String]
func splitOn(_ pat: String) -> (String) -> [String]{
    return {
        src in
        src.split(separator: pat, omittingEmptySubsequences: false).map({ String($0)})
    }
}

// tail :: [a] -> [a]
func tail<S: Sequence>(_ xs: S) -> [S.Element] {
    return Array(xs.dropFirst(1))
}

// take :: Int -> Sequence a -> [a]
func take<A, S: Sequence>(_ n: Int) -> (S) -> [A] where S.Iterator.Element == A {
    return { xs in Array(xs.prefix(n))}
}

infix operator • : CompositionPrecedence

// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
func • <A, B, C>(f: @escaping (B) -> C, g: @escaping (A) -> B) -> (A) -> C {
    return { x in f(g(x)) }
}

main()
1 Like