OK, the first day was awesome, I'm super excited about all of the solutions people posted. And I'm learning a lot! We've got a solid 20 people on the DEV leaderboard, which means there are still spots for 180 more -- use code 224198-25048a19 to join!
On to Day 2!
Day 2 of Advent of Code, and I'm pretty sure that Google is tired of me asking it questions every 15 seconds about "How Do I Do X in Rust."
Today's challenge involves an inventory system. Boxes have IDs that are a jumble of letters, and we've got a warehouse full of boxes to check. The first part asks us to analyze the frequency of letters in each ID. The second part gets into Hamming Distances, which are a familiar sight after mentoring on Exercism.
I got both parts working, and even part 2 ran pretty fast, but I'm not algorithmically happy with the double-loop (O(n^2)) runtime. Did anybody come up with anything tricky to do it more efficiently?
I'm going to post my solution in the comments like the rest of the cool kids.
How'd everybody else do?
Top comments (66)
Python solutions!
part 1
part 2 -- this one feels clunky to me.
My part one ended up looking super similar!
I ended up using
zip
to help with the comparison if the strings in part 2:Nice! Definitely think that's the easiest way to do number 1. Zip also makes sense for the second, though not using it allowed me to do the early return!
True!
10 points for the variable name “thrice!”
As a lispy thinker, my brain wants to tell you to make those for loops into comprehensions, but my work brain is telling me that my coworkers would have me drawn and quartered to make your "find-one-difference" a one-liner!
Kudos on how neat this looks. I find python and ruby to be difficult to make look "clean" when I write it.
Part 1
You can probably see my Python shining through as I implemented a custom Counter struct to help me out.
Part 2
Double for loop boooooooo! But it works and it's fast enough for now. I'm pretty happy with the mileage I got out of Iterators for this part.
This is very well documented and clear, easy-to-read code. This also makes me want to jump into Rust again (I've only hobbied around with it here and there).
Thanks! That really encouraging!
I love how you've used enumerate and skip together in your nested for loop. I struggled to find a clean solution like this.
Thanks! Yeah, I originally had a very manual nested for-loop set up, but after I got the tests passing, I decided to make an effort to do everything I could with iterators instead :) I've decided that the iterator module in Rust is where most of the magic that I'm missing from Python and Ruby lives.
This was still bothering me, but I found the
Itertools
crate and thetuple_combinations
method. Check out my updated solution in the thread.Itertools
makes iterators even more powerful.PHP
Ended up learning about a bunch of useful array functions like
array_values
,array_count_values
andarray_diff_assoc
for this one!Part 1:
Part 2:
I’m trying to use a broader range of languages than I do usually, so I figured I’d try not to use one I’d already used before through the days. I use bash all the time, so I thought I’d get it out of the way early.
This was not one of my better decisions, but worked fine!
Part 1 uses regular expressions; I could have used an associative array like some other people in the thread, but for some reason I went here first. The sort function wasn’t necessary, but helped with debugging.
Part 2 uses the same double
for
loop lamented elsewhere, but it gets the job done.Neither of these is what I’d call “fast”.
Woah, nice! It's always really cool to see bigger things done with Bash :)
P.S. Depending on your Bash version, you can save yourself some typing with
{a..z}
.Oh yes, good call, I missed that compaction.
Clojure (inefficient part 2)
Part 1:
Part 2:
I like the threaded use of
update
here in part 1 - my method used a transient map and returned a persistent copy at the end:Nice one. Is definitely faster than mine.
Javascript lazy solution
I don't have much time to solve the challenges :( So I'm just trying to get the stars.
part 1
Part 2
Thanks for hosting the private leaderboard! Never been on a leaderboard before lol so that'll be fun. :)
I am curious - how is everyone posting their code? Is there a code tag on here, like there is on Slack? Is everyone sharing screenshots? I haven't posted a whole lot on here yet, so I'm not sure of the best way to share code.
I'm using JS this year, so here's my day 2 solutions: not the prettiest / most succinct, but they work!
Part 1:
Part 2:
There's an enhanced form of markdown for code blocks: triple backticks for start and end, and if you immediately follow the opening backticks with the language you get syntax highlighting. Difficult to show raw markdown in markdown unfortunately.
Excellent, thank you! Much better than screenshots. :)
Enjoyed this one. My Kotlin solution:
Were you also annoyed that Kotlin has
.groupBy
but not.frequencies
?Have you thought about looking into
Sequence
? You could make your pairs function lazily? UsingList
means you're materializing the entirety of your double for-loop right away.The lack of
frequencies
didn't bother me - it's easy to add. And yes, I've been thinking for the rest of the day that I should use lazy sequences. In this case the execution time remains O(N²) but as you say the memory footprint becomes more like constant. Definitely a good practice when you can't find a better algorithm.A little late, to the party. I tried really hard to think of a solution to part 2 that only involved iterating the list once, but no luck. Here is my solution in Elixir.
Part one:
Part 2:
So this was a pain. I also ended up with a double loop (O(n2)) and couldn't think of anything better.
One thing I discovered during part two was that Crystal has Levenshtein distance as part of the standard library. It might have been a bit heavy going for what I needed, but it did the trick!
Bring on day 3!
High five for a beefy standard library! That’s awesome 😎
My solution with Python, not sure about the second part, not the most fastest solution I think regarding of performance.
Nice! Don’t forget about collections.Counter for part 1! I didn’t know about difflib though. Cool!
Thanks for the hint! Will do that later!
My edited solution for part one with collections Counter
Part 1: C# + LINQ = one-liner
Part 2
Part 1
Part 2
And from the output, I just copied and pasted the necessary characters that matched. That was faster than comming up with a custom method to do so.
Nice! Did you implement editdistance yourself, or is that an external library?
It is external. I found it via a quick google search. The edit distance measures how many operations - insertion, deletion or substitution - it takes to get from one string to the other. Since all the strings in the puzzle input have the same length, insertion and deletion do not come into play and it works out perfectly.
Ok that’s cool, just checking. Neat!
Terrible C++ solution for part 1 !
Terrible is better than never finished! And this looks pretty good to me, not knowing C++ if that makes you feel better 😄
I did my solutions at midnight last night, but I was surviving on very little sleep at the time, so the resulting code was below standard. I tried again this morning and felt better about it.
For anyone reading this, I'm still using the simple
lines
function from Day 1 which reads a file into a sequence of strings.Part 1
This was my solution last night:
This is embarrassing code. I totally forgot about the
frequencies
function, which is why I usedgroup-by
followed bycount
. But the 2filter
operations in the final calculation meant that the results of themap
get processed twice.My morning attempt fixed these:
This time I accumulated the 2/3 count values while processing the list, so it only goes through it once.
Part 2
Since each element needs to be compared to every other element, I can't see any way around the O(n2 ) complexity. Of course, each element should only be compared to the ones after it, so as to avoid comparing each one twice (since comparing A/B has the same result as comparing B/A).
When doing list processing, the only ways I know to refer to everything following an element are by using indexes (yuck) or with a
loop
. Unfortunately, I got fixated on the loop construct, and nested it:The other way you can tell that I wrote this on very little sleep was the use of ridiculously terse var names.
On reflection this morning, I realized that the inner loop should have been a
some
operation. This does a predicate test and terminates processing early, which is what I was doing manually with the innerloop
.Also, my
close
function has several issues. First is the name! I was thinking of "A is close to B", but when I saw it again this morning I realized that it's the same function name for closing a resource. Something else that bothers me is that it processes the entirety of each string before returning, when a false result can usuall terminate early. Finally, a minor issue is that the anonymous function#(when (= %1 %2) %1)
would be more idiomatic to use a singleton set on%1
to compare to%2
.The
nearly=
function now returns a string, rather than the array of characters, but hasn't changed much. I was still unsatisfied with it not terminating the test early, so I rewrote it to be faster. However, the resulting code lacks any kind of elegance, and I'm not convinced that it's worth the effort:Hopefully I'll get some sleep before attempting day 3. 😊