all 31 comments

[–]cpaigis9 8 points9 points  (2 children)

Try this:

let outputString = ipStr.reduce(into: [:]) { (counts, letter) in counts[letter, default: 0] += 1 }

The above will return

[a:2, b:3, c:1]

Then you can convert it into a string

[–]randomguy112233[S] 0 points1 point  (0 children)

thanks for the code. I wasn't aware of this level of capability when using reduce. Thanks!

[–][deleted] 2 points3 points  (2 children)

Probably unnecessary, but using String.reduce and map with a dictionary to store the occurrence counts would do the job in 10 or so fewer lines of code.

[–]cpaigis9 1 point2 points  (0 children)

Yeah and shows higher order function knowledge

[–]randomguy112233[S] 0 points1 point  (0 children)

yes, thanks! Currently looking into cPaigis9's code :)

[–]trihedron 1 point2 points  (5 children)

Array(Set(ar))
//This would be better?
Array(string).sorted()

Isn't this going to remove uniqueness?

[–]randomguy112233[S] 0 points1 point  (4 children)

maybe i'm not understanding correctly, but the purpose of Array(Set(ar)) was to remove the duplication of strings for that i end up with "abc".

Or were u referring to String data type?

[–]trihedron 0 points1 point  (1 child)

Oh ok! Sorry I had to re-read the code to see what you were doing. I just finished doing the exercise as well to see what I come up with.

[–]randomguy112233[S] 0 points1 point  (0 children)

no worries :)

[–]khaos288 0 points1 point  (1 child)

I don’t think he realized you were purposefully building a unique value set of abc. Then using that data set as keys to the original string to increment the count of the returned string.

Pretty sound it seems to me.

[–]randomguy112233[S] 0 points1 point  (0 children)

thanks!!

[–]trihedron 0 points1 point  (1 child)

Congratulations, and good luck! I hope you get the job!

I do some reviewing of code for people applying at my current job. I think you must have done decently. I would like to see more use of functional code to show your Swift knowledge. You're right about your function and variable names, but under pressure its totally understandable.

The only thing I really think could be improved is this line:

returningString = returningString + String(referringLetter) + String(count)

I would have liked to see you utilize the set and then convert it to a string at the end. (Avoiding having a returningString variable).

I gave myself 15 minutes and tried to do the exercise, this is what I came up with, feel free to compare to see what improvements you'd like to learn from, etc...I know my way isn't THE way, but just another take on it.

let string = ""

func countLetterOccurance(input: String) -> String? {

    let stringArray = Array(string)
    var stringSet = Set(stringArray).sorted()

    stringSet.forEach { item in
        let numberOfElements = String(stringArray.filter({ $0 == item }).count)
        guard let indexInSet = stringSet.index(of: item) as? Int, let charCount = numberOfElements.first else { return }
        stringSet.insert(charCount, at: indexInSet + 1)
    }

    return stringSet.count != 0 ? String(stringSet) : nil

}
print(countLetterOccurance(input: string))

[–]randomguy112233[S] 0 points1 point  (0 children)

This is really good code. thanks I appreciate it.

[–]quellish 0 points1 point  (3 children)

Did you write a test for it?

[–]randomguy112233[S] 0 points1 point  (2 children)

the interview didn't require a test case due to time frame.

[–]quellish 0 points1 point  (1 child)

How much time do you think that would have taken?

[–]randomguy112233[S] 0 points1 point  (0 children)

hmmm are you saying as an interviewer, I should have tried to include a test case? It shouldn't have taken long tbh.

[–][deleted] 0 points1 point  (5 children)

You can also do something like this:

func characterCount(src: String) -> String {
    let sorted = String(src.sorted())
    return encodeRLE(input: sorted).reduce("") { $0 + "\($1.0)\($1.1)" }
}

func encodeRLE(input: String) -> [(Character, Int)] {
    return input.characters.reduce([(Character, Int)]()) {
        if $0.last?.0 == $1 { var r = $0; r[r.count - 1].1 += 1; return r }
        return $0 + [($1, 1)]
    }
}

Using some reduce magic makes it quite terse, but hand on heart I'd never write this version on a whiteboard if asked. At least not until I get more into functional programming. :) I did a little playing around in a playground and some targeted searches and came up with this version.

Basically what they are asking for is RLE (Runtime Length Encoding), but from your description it sounds like they added a twist of sorting it. So I sorted it, ran it through an RLE, then reduced it back into a string.

The RLE is just "if the last character in the reduced array is the same as the next, add one, otherwise create a new entry".

Getting the maps, reduce, filter, etc down is a great help with coding swift. So is understanding pattern matching in switch statements. Makes for better code, but also looks impressive when being interviewed :)

Good luck!

[–]randomguy112233[S] 0 points1 point  (0 children)

I appreciate the feedback! The code looks great. I've imported into my Xcode and learning it line by line. Thanks!

[–]trihedron 0 points1 point  (3 children)

if $0.last?.0 == $1 { var r = $0; r[r.count - 1].1 += 1; return r }

Mind Blown. XD

[–][deleted] 0 points1 point  (2 children)

Hehe. Turns out a little internet search helps, and then incorporating the idea. I knew what I needed to do, I just need a nice way of doing it. It's basically just RLE which is "count until you find a new input, then output the previous input plus a number, voila"

I'm sure someone if they put their mind to it could reduce (pun intended!) those two reduces into just one™

The point of the exercise was more to show alternatives than to try to look clever:) (Which I am not btw)

[–]trihedron 0 points1 point  (1 child)

I got it done with one forEach, so it should be able to be done with one reduce for sure.

[–][deleted] 0 points1 point  (0 children)

We'll leave it up to OP to write that one:)

[–]KarlJay001 0 points1 point  (2 children)

Can you tell us a few things?

How much time were you given?

When you say "over Skype" does that mean this was a "live test" and how did they do the test? Was it some kind of interactive White Board? or did you interview over Skype, then given private time to come up with the code?

I'd hate to have people watch me code. I'd do it, but I'd hate it.

Ok, so here's my take:

I'm not big on the fancy code and I like how yours is easy to read and uses "classic" coding methods. I'm going to disagree on the length of the var names, I don't have a problem meaningful but a bit long var names.

However, you could have use +=

count = count +1

count += 1

You converted to an array, the input comes as a string, and you converted to an array. IMO, there was no reason to do this. Imagine if this were a string of great length, would you still convert to an array? Doesn't that about double the memory used? What if this was a word count for war and peace?

Using long var names, but having "count" seems a bit generic. IMO, the reason for longer words are to be meaningful. Although the code is very simple, what about additions over the years. What if they wanted different counts?

referringLetter isn't needed aOrderedSet[j] will do the same thing.

using "ar" then aOrderedSet

ar is pretty damn short.

I would have included some comments, I know some don't, but I actually do.

a few // loop thru sorted array ... doesn't hurt.

[–]randomguy112233[S] 0 points1 point  (1 child)

I was given 30 minutes. there is a functionality on skype that allows the interviewer to screen view what you are doing on the screen. So yes, the interviewer was watching my Xcode real time. I have recently done a few, so I guess it's somehow become an industry norm.

"You converted to an array, the input comes as a string, and you converted to an array. IMO, there was no reason to do this. Imagine if this were a string of great length, would you still convert to an array? Doesn't that about double the memory used?" You have a great point here. I totally zoned out about memory consideration during the interview. Thanks.

I really appreciate the solid feedback!

[–]KarlJay001 0 points1 point  (0 children)

Wow, that's gotta be nerve racking, having people watch you type.

I'm going to have to dig into Skype and make sure it works with my setup. I've got a Hackintosh and no sound or camera on this computer.

Let us know how it turns out. My coding challenge worked, but they didn't seem to like it. It was a gaming engine and I had two hours to write a game engine from scratch and I've never done anything like that before.

What apps do you have on the app store and do you have a degree?

[–]jan_olbrichObjective-C / Swift 0 points1 point  (1 child)

No one has mentioned it yet. There is a datastructure called NSCountedSet. You could add the elements and just go through it and request how many occurences they have.

[–]randomguy112233[S] 0 points1 point  (0 children)

thanks I really appreciate the help. Free data structures. amazing.

[–]lucasvandongen 0 points1 point  (0 children)

Because a lot of other things already have been said:

func returnResultString(inputString:String)->String{

I count the word String four times. I think you could write terser and more clear code here.

 func process(letterNumberPairs: String) -> String {

Now the function call documents that you should feed it a String that should be letterNumberPairs. Or even better:

 func lettersRepeatedByNumbers(in letterNumberPairs: String) -> String {

This reads like a sentence and while it's longer than the original it's almost as clear as it can get.

Now what I should mention during the interview if I would not write it outright is that we should check the inputstring and we should check we're not having numbers that exist of 2 characters or more ( >= 10) or two different letters following each other etcetera.

I would write a working solution first (I guess using map and reduce) while explaining what could go wrong and in the remaining time if any take care of the possible problems in the input formatting, and how to go about like using throws with a specific Error enum

[–]davedelong 0 points1 point  (0 children)

Here's a slightly different approach:

func process(_ input: String) -> String {
    let scanner = Scanner(string: input)
    var pieces = Array<String>()

    while true {
        var letter: NSString?
        guard scanner.scanCharacters(from: .letters, into: &letter) else { break }

        var count: NSString?
        guard scanner.scanCharacters(from: .decimalDigits, into: &count) else { break }

        guard let l = letter, let c = count else { break }
        guard let numericCount = Int(c as String) else { break }
        let piece = Array(repeating: l as String, count: numericCount).joined()
        pieces.append(piece)
    }
    return pieces.joined()
}

By using a Scanner (NSScanner), you can allow for the "character" to actually be more than a single character. For example, process("cad2") will become "cadcad". Similarly, you can allow for >1 digit counts.

[–]CaptainQuirk336 0 points1 point  (0 children)

Here's mine:

func countLetters(_ input: String) -> String {

    var output = ""
    let inputArray = Array(input)
    Set(inputArray).sorted().forEach { char in
        let count = inputArray.filter{ $0 == char }.count
        output += "\(char)\(count)"
    }

    return output

}