I ended up using zip to help with the comparison if the strings in part 2:
#!/usr/bin/env python
fromcollectionsimportCounterdeffind_similar_box_ids(all_box_ids):forbox_id_1inall_box_ids:forbox_id_2inall_box_ids:# This is kind of a naive Levenshtein distance!
letter_pairs=zip(box_id_1,box_id_2)duplicates=list(filter(lambdapair:pair[0]==pair[1],letter_pairs))iflen(duplicates)==len(box_id_1)-1:return''.join(pair[0]forpairinduplicates)if__name__=='__main__':withopen('2-input.txt')asbox_file:all_box_ids=box_file.read().splitlines()print(find_similar_box_ids(all_box_ids))
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!
I'm a Staff Software Engineer at Cisco. I specialize in Python and Go, building functional, practical, and maintainable web systems leveraging Kubernetes and the cloud. Blog opinions are my own.
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.
I'm a Staff Software Engineer at Cisco. I specialize in Python and Go, building functional, practical, and maintainable web systems leveraging Kubernetes and the cloud. Blog opinions are my own.
You can probably see my Python shining through as I implemented a custom Counter struct to help me out.
usestd::collections::HashMap;// Part 1/// A histogram of the characters in a string.structCounter{letters:HashMap<char,usize>,}implCounter{pubfnnew(word:&str)->Self{letmutletters=HashMap::new();forletterinword.chars(){letcurrent_reference=letters.entry(letter).or_insert(0);*current_reference+=1;}Self{letters}}pubfncount_value(&self,number:usize)->usize{self.letters.values().filter(|count|**count==number).count()}}/// Calculates a checksum for id strings./// /// The checksum is the number of id's with at least one set of exactly/// two of a letter times the number of id's with at least one set of/// exactly three of a letter. If it has more than onepubfnchecksum(text:&str)->usize{letmuttwos=0;letmutthrees=0;text.lines().map(|id|Counter::new(id)).for_each(|counter|{ifcounter.count_value(2)!=0{twos+=1;}ifcounter.count_value(3)!=0{threes+=1;}});twos*threes}
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.
// Part 2/// Finds the letters that are shared between the two prototype fabric/// box ids./// /// These ids are the only two that differ from each other by exactly/// one letter.pubfnprototype_ids_common_letters(text:&str)->String{letids:Vec<&str>=text.lines().collect();for(i,s1)inids.iter().enumerate(){fors2inids.iter().skip(i){ifhamming_distance(s1,s2)==1{returncommon_letters(s1,s2);}}}String::new()}/// Calculates the "Hamming Distance" between two strings/// /// Hamming distance is the number of characters who are different/// between the two strings when the corresponding indices are compared/// in each stringfnhamming_distance(s1:&str,s2:&str)->usize{s1.chars().zip(s2.chars()).filter(|(c1,c2)|c1!=c2).count()}/// Returns the letters that are the same (and in the same place)/// between the two stringsfncommon_letters(s1:&str,s2:&str)->String{s1.chars().zip(s2.chars()).filter(|(c1,c2)|c1==c2).map(|(c1,_c2)|c1).collect()}
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).
I'm a Staff Software Engineer at Cisco. I specialize in Python and Go, building functional, practical, and maintainable web systems leveraging Kubernetes and the cloud. Blog opinions are my own.
I'm a Staff Software Engineer at Cisco. I specialize in Python and Go, building functional, practical, and maintainable web systems leveraging Kubernetes and the cloud. Blog opinions are my own.
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 the tuple_combinations method. Check out my updated solution in the thread. Itertools makes iterators even more powerful.
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:
(nsaoc.aoc2)(defnreduce-twos-threes"check the given frequency map n for twos or threes matches, and update
the memo map to indicate if the string has a match. Used for a reducer."[memon](let[t-memo(transientmemo)](if(some(fn[[kv]](=v2))n)(assoc!t-memo:twos(inc(:twost-memo))))(if(some(fn[[kv]](=v3))n)(assoc!t-memo:threes(inc(:threest-memo))))(persistent!t-memo)))(defnchecksum[input](let[sum-maps(mapfrequenciesinput)twos-threes(reducereduce-twos-threes{:twos0:threes0}sum-maps)](*(:twostwos-threes)(:threestwos-threes))))
packageadventofcode2018.day2importjava.io.FilefunrepeatCounts(s:String):Set<Int>=s.groupBy{it}.values.map{it.size}.toSet()fundifference(s1:String,s2:String):Int=s1.zip(s2,{x,y->if(x==y)0else1}).sum()funcommon(s1:String,s2:String):String=s1.zip(s2,{x,y->if(x==y)x.toString()else""}).joinToString(separator="")fun<T>pairs(xs:Collection<T>):List<Pair<T,T>>=when{xs.isEmpty()->listOf()else->{valhead=xs.first()valtail=xs.drop(1)(tail.map{headtoit})+pairs(tail)}}funmain(args:Array<String>){valfile=if(args.isEmpty())"input.txt"elseargs[0]valinput=File(file).readLines().map(String::trim)// Part 1valcounts=input.map(::repeatCounts)valnumPairs=counts.filter{s->s.contains(2)}.sizevalnumTriples=counts.filter{s->s.contains(3)}.sizeprintln("Part 1 checksum: ${numPairs * numTriples}")// Part 2valdifferentByOnePairs=pairs(input).filter{(x,y)->difference(x,y)==1}println(differentByOnePairs.map{(x,y)->common(x,y)})}
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? Using List 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.
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!
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.
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.
#!/bin/bashtwos=0
threes=0
function sort_str (){# Bubble Sort ^_^local sl="$1"changed=1
while[["$changed"-eq 1 ]];do
changed=0
for i in$(seq 1 $((${#sl}-1)));do
if[[${sl:$(( i -1)):1}>${sl:$i:1}]];then
echo"${sl:$(( i -1)):1} > ${sl:$i:1}">>sort_progress
changed=1
if[[$i-eq 1 ]];then
sl=${sl:1:1}${sl:0:1}${sl:2}else
sl=${sl:0:$(( i -1))}${sl:$i:1}${sl:$((i-1)):1}${sl:$i+1}fi
fi
done
done
echo$sl}re_2_str=""re_3_str=""for a in a b c d e f g h i j k l m n o p q r s t u v w x y z;do
re_2_str+="^[^$a]*$a[^$a]*$a[^$a]*$|"
re_3_str+="^[^$a]*$a[^$a]*$a[^$a]*$a[^$a]*$|"done
re_2_str="(${re_2_str%|})"re_3_str="(${re_3_str%|})"while read line
do
echo-n"$line: $(sort_str $line): "if[["$line"=~ $re_2_str]];then
twos=$(( twos +1))echo-n"2 "fi
if[["$line"=~ $re_3_str]];then
threes=$(( threes +1))echo-n"3 "fi
echo
done <2.1.input
echo"Twos: $twos"echo"Threes: $threes"echo"x: $(( twos * threes ))"
Part 2 uses the same double for loop lamented elsewhere, but it gets the job done.
#!/bin/bashwhile read line
do
while read comp_line
do
same_string=""ndiffs=0
for i in$(seq 0 $((${#line}-1)))do
if[["${line:$i:1}"=="${comp_line:$i:1}"]];then
same_string+=${line:$i:1}else
ndiffs=$(( ndiffs+1 ))fi
done
if[["$ndiffs"-eq 1 ]];then
echo"$line: $comp_line: $same_string"exit
fi
done <2.2.input
done <2.2.input
I'm a Staff Software Engineer at Cisco. I specialize in Python and Go, building functional, practical, and maintainable web systems leveraging Kubernetes and the cloud. Blog opinions are my own.
never met a part of the stack I didn't like. sr. engineer at clique studios in chicago, perpetual creative hobbyist, bird friend, local gay agenda promoter. she/her. tips: https://ko-fi.com/carlymho
I'm a Staff Software Engineer at Cisco. I specialize in Python and Go, building functional, practical, and maintainable web systems leveraging Kubernetes and the cloud. Blog opinions are my own.
I'm a Staff Software Engineer at Cisco. I specialize in Python and Go, building functional, practical, and maintainable web systems leveraging Kubernetes and the cloud. Blog opinions are my own.
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.
I'm a Staff Software Engineer at Cisco. I specialize in Python and Go, building functional, practical, and maintainable web systems leveraging Kubernetes and the cloud. Blog opinions are my own.
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.
fromcollectionsimportdefaultdictfromdifflibimportSequenceMatcherfromoperatorimportitemgetterdefsimilar(a,b):returnSequenceMatcher(None,a,b).ratio()defmain():part_one()part_two()defpart_one():withopen('input.txt','r')asinput_file:lines=[lineforlineininput_file]word_twice_count=0word_three_times_count=0forlineinlines:word_count_dic=defaultdict(int)has_char_twice=Falsehas_char_three_times=Falseforcharinline:word_count_dic[char]+=1forkeyinword_count_dic.keys():ifword_count_dic[key]==2:has_char_twice=Trueelifword_count_dic[key]==3:has_char_three_times=Trueifhas_char_twice:word_twice_count+=1ifhas_char_three_times:word_three_times_count+=1checksum=word_twice_count*word_three_times_countprint'checksum is '+str(checksum)defpart_two():withopen('input.txt','r')asinput_file:lines=[lineforlineininput_file]similarity_strings=[(line,comparing_line,similar(line,comparing_line))forlineinlinesforcomparing_lineinlinesiflineisnotcomparing_line]most_similair_strings=max(similarity_strings,key=itemgetter(2))result_word=''.join([char_word_oneforchar_word_one,char_word_twoinzip(most_similair_strings[0],most_similair_strings[1])ifchar_word_one==char_word_two])printresult_wordif__name__=='__main__':main()
My solution with Python, not sure about the second part, not the most fastest solution I think regarding of performance.
I'm a Staff Software Engineer at Cisco. I specialize in Python and Go, building functional, practical, and maintainable web systems leveraging Kubernetes and the cloud. Blog opinions are my own.
defpart_one():word_twice_count=0word_three_times_count=0withopen('input.txt','r')asinput_file:forlineininput_file:line_counter=Counter(line)if2inline_counter.values():word_twice_count+=1if3inline_counter.values():word_three_times_count+=1checksum=word_twice_count*word_three_times_countprint'checksum is '+str(checksum)
My edited solution for part one with collections Counter
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!
require"levenshtein"classFabricBoxgetterid@letter_map:Hash(String,Int32)definitialize(@id:String)@letter_map=@id.split("").reduce(Hash(String,Int32).new(default_value: 0))do|acc,letter|acc[letter]=acc[letter]+1accendenddefhas_exactly?(letters:Int32):Bool@letter_map.values.any?{|count|count==letters}endendclassFabricBoxCollectiongetterboxes@boxes:Array(FabricBox)definitialize(ids:Array(String))@boxes=ids.map{|id|FabricBox.new(id)}enddefchecksum@boxes.count{|box|box.has_exactly?2}*@boxes.count{|box|box.has_exactly?3}enddeffind_close_id@boxes.eachdo|box_i|@boxes.eachdo|box_j|ifLevenshtein.distance(box_i.id,box_j.id)==1characters_i=box_i.id.split("")characters_j=box_j.id.split("")char_pairs=characters_i.zip(characters_j)returnchar_pairs.reduce("")do|acc,(char_i,char_j)|acc+=char_iifchar_i==char_jaccendendendendendendputs"--- Day 2: Inventory Management System ---"input=File.read_lines("./02/input.txt")collection=FabricBoxCollection.new(input)puts"Checksum: #{collection.checksum}"puts"Common letters: #{collection.find_close_id}"
I'm a Staff Software Engineer at Cisco. I specialize in Python and Go, building functional, practical, and maintainable web systems leveraging Kubernetes and the cloud. Blog opinions are my own.
I am using Advent of Code to learn Golang, and here is the solution I came up with. Suggestions for improvements are always welcome!
Part 1:
packagemainimport("bufio""fmt""os""strings")// readLines reads a whole file into memory// and returns a slice of its lines.funcreadLines(pathstring)([]string,error){file,err:=os.Open(path)iferr!=nil{returnnil,err}deferfile.Close()varlines[]stringscanner:=bufio.NewScanner(file)forscanner.Scan(){lines=append(lines,scanner.Text())}returnlines,scanner.Err()}funcmain(){boxes,err:=readLines("input")iferr!=nil{panic(err)}vartwos,threesintfor_,box:=rangeboxes{// Iterate through each box letter-by-letter and check if letters appear// two or three times:two,three:=false,falsefor_,letter:=rangebox{if!two&&strings.Count(box,string(letter))==2{twos++two=true}elseif!three&&strings.Count(box,string(letter))==3{threes++three=true}iftwo&&three{// We already found the maximum number of appearing letters we countbreak}}}checksum:=twos*threesfmt.Printf("Checksum is: %d\n",checksum)}
Part 2:
packagemainimport("bufio""fmt""os")// readLines reads a whole file into memory// and returns a slice of its lines.funcreadLines(pathstring)([]string,error){file,err:=os.Open(path)iferr!=nil{returnnil,err}deferfile.Close()varlines[]stringscanner:=bufio.NewScanner(file)forscanner.Scan(){lines=append(lines,scanner.Text())}returnlines,scanner.Err()}funcfindSimilar(boxes[]string)string{// For each box name we remove the i'th element and store the rest of the// name inside a map:visited:=make(map[int]map[string]bool)for_,box:=rangeboxes{// Go through each box:fori:=rangebox{// Remove i'th character from box name:subname:=box[:i]+box[i+1:]// Check if we have already visited a similar box name:_,ok:=visited[i][subname]if!ok{// If we have never encountered a similar box name, add it:_,ok:=visited[i]if!ok{sub:=make(map[string]bool)visited[i]=sub}visited[i][subname]=true}else{// We have encounter a similar box name, return it:returnsubname}}}return""}funcmain(){boxes,err:=readLines("input")iferr!=nil{panic(err)}subname:=findSimilar(boxes)fmt.Println(subname)}
My idea was to use a dictionary and store the subnames and see if we encounter one we have already visited. Since there should only be one match we can immediately return it. I learned a lot about using maps in Golang!
I am also using Python that I have more experience with to cross check solutions. I have tried to implement it with readability in mind, not performance.
I lost about 10 minutes to my son stalling getting into bed.
Kotlin Solution
Answer 1
Once again a fairly straightforward opener. Just run through, do some simple frequency checking and spit back the results. I think this is technically O(n2) but it moved fast enough for me. (And in a more lazy language, it ends up being closer to O(n) anyway)
As Ryan said, this is just Hamming distance with the added wrinkle that you need to throw away the differences while counting them. Lots of optimization room here, but once again, we shave off just under half the possibilities by only doing unique combinations and going from a raw n^2 to (n*(n+1))/2.
At around 10 ms (calculated by shuffling my input 1000 times), I don't think there's an easy way to make this noticeably faster at this point without a more esoteric method.
fun<A,B>List<A>.cartesian(transform:(A,A)->B):Sequence<B>{returninit.asSequence().mapIndexed{i,a->drop(i+1).asSequence().map{b->transform(a,b)}}.flatten()}fun<A>Pair<A,A>.same()=first==secondfun<A>Pair<A,A>.different()=first!=secondfunanswer2(input:List<String>)=input.cartesian{s1,s2->s1.zip(s2)}.find{// find short circuits on Sequenceit.count(Pair<Char,Char>::different)==1}?.filter{it.same()}?.joinToString(""){it.first.toString()}
Not happy about the double loop through the input file... but also can't think of a way to avoid it! It occurred to me that sorting the list first might improve the chances of finding a match earlier in the loop since all similar strings would be together, but thinking about it, perhaps there's equal chance that the similar strings would end up being sorted to the bottom of the list and it wouldn't help at all :/
Another thought - many of the input strings are v. similar, maybe they could be grouped into sets early on (e.g. based on the first 4 chars or something) and then you only look for similar strings in the same set?
`python3 2_1.py` 100 times
real 0m4.744s
user 0m3.129s
sys 0m0.967s
`go run 2_1.go` 100 times
real 0m37.649s
user 0m28.807s
sys 0m14.105s
go build
`./2_1` 100 times
real 0m0.709s
user 0m0.327s
sys 0m0.280s
Part 2 got interesting. I needed to generate all pairs for every product ID. I made my Hamming Distance function also return the common letters. Then tied it all together by running each pair through the Hamming Distance function and getting the lowest.
I'm a Staff Software Engineer at Cisco. I specialize in Python and Go, building functional, practical, and maintainable web systems leveraging Kubernetes and the cloud. Blog opinions are my own.
Making hamming distance return common letters is a slick way to go. I was looking at the duplication between the hamming distance and common letters functionality in my solution and was a little bummed about it, but I couldn't figure out a good way to do it.
publicclassInventoryManagement{publicintGetCheckSum(IEnumerable<string>inventory){returnGetCheckSum(GetCheckSumCandidates(inventory));}publicintGetCheckSum(IEnumerable<InventoryChecksumCandidates>candidates){varchecksumPieces=candidates.GroupBy(gb=>gb.DuplicateCount).Select(s=>new{s.Key,Count=s.Count()}).ToList();Debug.Assert(checksumPieces.Count()==2);returnchecksumPieces[0].Count*checksumPieces[1].Count;}publicIEnumerable<InventoryChecksumCandidates>GetCheckSumCandidates(IEnumerable<string>inventory){List<InventoryChecksumCandidates>list=newList<InventoryChecksumCandidates>();foreach(varitemininventory){varchecksumCandidate=item// make everything same case (just in case).ToLower()// where it's a letter.Where(char.IsLetter)// Group by the default (letter).GroupBy(gb=>gb)// Project the found values into their new type.Select(s=>newInventoryChecksumCandidates(){DuplicateCharacter=s.Key.ToString(),DuplicateCount=s.Count()});if(checksumCandidate!=null){list.AddIfNotNull(checksumCandidate.FirstOrDefault(f=>f.DuplicateCount==2));list.AddIfNotNull(checksumCandidate.FirstOrDefault(f=>f.DuplicateCount==3));}}returnlist;}}
publicclassPrototypeFabricFinder{publicstringGetProtoTypeFabric(IEnumerable<string>inventory){varinventoryPermutations=SwapCharForEachInventoryPermutation(inventory);varfoundDuplicateish=inventoryPermutations// Group by default.GroupBy(gb=>gb)// We only want the instance where there's more than// one in the group by (the prototype fabric).Where(w=>w.Count()>1).Select(s=>new{s.Key}).FirstOrDefault();// Return the prototype fabric, minus the "different" single characterreturnfoundDuplicateish.Key.Replace("*","");}/// <summary>/// Builds and returns a <see cref="IEnumerable{string}"/>/// containing every permutation of iventory items, with/// one character (*) swapped in at a differing index./// /// I have no idea if that makes sense./// </summary>/// <param name="inventory">each inventory id.</param>/// <returns></returns>privateIEnumerable<string>SwapCharForEachInventoryPermutation(IEnumerable<string>inventory){List<string>list=newList<string>();foreach(varitemininventory){// Create new strings and add them to the list.// The new strings will be a copy of the original,// with a single character (the current index) swapped in with "*"for(varindex=0;index<item.Length;index++){varcharArrayOfItem=item.ToCharArray();charArrayOfItem[index]='*';list.Add(newstring(charArrayOfItem));}}returnlist;}}
I’m a web developer & data visualizer working at a think tank in D.C. I'm a self-taught dev trying to better my skills. I spend most of my time on the front end of the stack.
Location
Washington, D.C.
Work
Lead Developer at Center for Strategic and International Studies
Part 1
First creates an Object to track the # of times a letter appears in a string. That gets converted to a Set to filter out any duplicate values to account for situations where 2 or more letters appeared twice (as the string only gets counted once for the check sum).
Part 2
I struggled with this one, so I'm sure there's a cleaner/more efficient way to do this. This takes each line of the input and compares it to the other lines, checking the characters and tracking the # of differences and the position of the last accounted for difference. The loops are set to break when a result with only 1 difference is found to prevent unnecessary looping.
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.
This is embarrassing code. I totally forgot about the frequencies function, which is why I used group-by followed by count. But the 2 filter operations in the final calculation meant that the results of the map get processed twice.
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 inner loop.
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:
const{readFile}=require('./readLines');(async()=>{constlines=awaitreadFile('02-input.txt');lettwoLettersCount=0;letthreeLettersCount=0;for(letlineoflines){constfrequencyMap={};for(constcharofline.split('')){frequencyMap[char]=(frequencyMap[char]||0)+1;}consthasTwoLetters=Object.values(frequencyMap).some(frequency=>frequency===2);consthasThreeLetters=Object.values(frequencyMap).some(frequency=>frequency===3);twoLettersCount+=+hasTwoLetters;threeLettersCount+=+hasThreeLetters;};constchecksum=twoLettersCount*threeLettersCount;console.log(`The checksum is ${checksum}`);})();
02b.js
const{readFile}=require('./readLines');// Compares two strings to see if they differ by one char and which one functioncompare(string1,string2){constlength=string1.length;letdifferentChars=0;letdifferIndex;for(leti=0;i<length;i++){if(string1.charAt(i)!==string2.charAt(i)){differentChars++;differIndex=differentChars===1?i:undefined;}}return{differByOneChar:differentChars===1,differIndex};}// Compare each strings to every other string // and get the common letters in case the differByOneChar is truefunctiongetCommonLetters(ids){constidsCount=ids.length;for(leti=0;i<idsCount;i++){constid=ids[i];for(letj=i+1;j<idsCount;j++){const{differByOneChar,differIndex}=compare(id,ids[j]);if(differByOneChar){returnid.slice(0,differIndex)+id.slice(differIndex+1);}}}}(async()=>{constlines=awaitreadFile('02-input.txt');constcommonLetters=getCommonLetters(lines);console.log(`The common letters are ${commonLetters}`);})();
I'm not super thrilled with my Day 2 code, but I haven't really had the time to tweak it with everything going on at work currently.
Swift Solutions
Part 1
This one was fairly simple. Just count how many times each letter appears in each String and act appropriately. I do like the fact that a Swift String type is really an Array of Characters.
// Part 1: Find the checksumsfunccalculateChecksum(_idCodes:[String])->Int{vardoubles=0vartriples=0idCodes.map{(boxID)invardoubleTrue=falsevartripleTrue=falseforcharinboxID{letcount=boxID.filter{$0==char}.countifcount==2{doubleTrue=true}ifcount==3{tripleTrue=true}}doubles+=doubleTrue?1:0triples+=tripleTrue?1:0}returndoubles*triples}letchecksum=calculateChecksum(boxIDs)print("Boxes checksum is: \(checksum)")
Part 2
This one feels clunky, if I get a chance I'll revisit it.
I break on the first hit for a solution to short circuit everything and return the answer, this can help a lot with so many Characters to test.
I use zip(_:_:) with a filter and count to quickly test how many differences there are in the same positions. When I see two strings that differ by one character in the same position I move to the next step.
In the second part I cast the Arrays into Set types so that I can use the Collection Algebra features to quickly find the actual character to remove by subtracting one collection from the other. With that done I can just remove it from the source Array and return what's left.
// Part 2: Box finderfuncfindTheBoxes(_idCodes:[String])->String{varresult=""vardifferceCount=0forboxIDinidCodes{ifdifferceCount==1{break}forcodeinidCodes{differceCount=zip(boxID,code).filter{$0!=$1}.countifdifferceCount==1{letdiff=Set(boxID).subtracting(code)ifletcharToRemove=diff.first{result=boxIDifletfoo=result.index(of:charToRemove){result.remove(at:foo)break}}}}}returnresult}lettheBoxes=findTheBoxes(boxIDs)print("Matching box ID is: \(theBoxes)")
Normally I would import Foundation here so that I could just use NSOrderedSet and skip a few steps. I wanted to try and keep it all in the Swift Standard Library though, so I didn't!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace Day2Part1
{
class Program
{
static void Main(string[] args)
{
string[] inputTxt = File.ReadAllLines(@"C:\Users\Ben\Desktop\adventOfCode2018\Day 2\input.txt");
List<int> TwoMatchList = new List<int>();// List of id's to find box in the input array
List<int> ThreeMatchList = new List<int>();// list of id's to find box in the input array
foreach (string boxID in inputTxt)
{
int id = Array.IndexOf(inputTxt, boxID);
var distinctLetters = boxID.Select(x => x).Distinct().ToList();
foreach (char letter in distinctLetters)
{
var matches = boxID.Where(x => x == letter).ToList();
switch (matches.Count)
{
case 2:
//if there's exactly 2, and it's not already in the list then add it to the TwoMatchList
if (!TwoMatchList.Contains(id))
{
TwoMatchList.Add(id);
}
break;
case 3:
//if there's exactly 3, and it's not already in the list then add it to the ThreeMatchList
if (!ThreeMatchList.Contains(id))
{
ThreeMatchList.Add(id);
}
break;
default:
//don't add to any list
break;
}//end switch
}//end foreach letter
}//end foreach boxID
int checksum = TwoMatchList.Count * ThreeMatchList.Count;
Console.WriteLine("Number of boxes with exactly two matching letters {0}", TwoMatchList.Count);
Console.WriteLine("Number of boxes with exactly three matching letters {0}", ThreeMatchList.Count);
Console.WriteLine("Checksum: {0}", checksum);
Console.WriteLine();
Console.WriteLine("Now for part two!");
//empty Dictionary to hold any boxes we find and the index of the letter that's different
Dictionary<string, int> savedIDsAndIndexes = new Dictionary<string, int>();
//empty string to hold the answer
string answer = "";
//Loop through to test and find correct boxes ==>> this could be a foreach loop
for (int i = 0; i < inputTxt.Length; i++)
{
string boxID = inputTxt[i];
for (int j = inputTxt.Length - 1; j >= 0; j--)
{
List<int> mismatchIndexes = new List<int>();
string testBoxID = inputTxt[j];
for (int k = 0; k < boxID.Length; k++)
{
if (boxID[k] != testBoxID[k])
{
mismatchIndexes.Add(k);
}
}
if (mismatchIndexes.Count == 1)
{
savedIDsAndIndexes.Add(boxID, mismatchIndexes.First());
}
}
}
Console.WriteLine("number of correct boxes found: {0}", savedIDsAndIndexes.Count);
answer = savedIDsAndIndexes.Keys.First().Substring(0, savedIDsAndIndexes.Values.First())
+ savedIDsAndIndexes.Keys.First().Substring(savedIDsAndIndexes.Values.First() + 1);
Console.WriteLine("here are the ids:");
foreach (var id in savedIDsAndIndexes)
{
Console.WriteLine(id.Key);
}
Console.WriteLine("The common characters are:\n{0}", answer);
Console.WriteLine("saved index: {0}", savedIDsAndIndexes.Values.First());
}
}
}
This one was difficult for me, but I eventually got it! I'm also not happy about that double for loop in part 2, but I think I sped it up by removing the elements as I compared them?
I'm pretty sure part 2 has to be O(n2) but you can cut down on the total number of iterations by only looking forward...here's my solution (implemented in Julia)
function inventory_management_diff(input)word_idx=0whileword_idx<length(input)word_idx+=1word1=input[word_idx]forword2ininput[word_idx+1:end]ch_diff=one_char_diff(word1,word2)ifch_diff!=nothingreturnstring(word1[1:ch_diff-1],word1[ch_diff+1:end])endendendendfunction one_char_diff(word1,word2)found_diff=nothingch_idx=0whilech_idx<length(word1)ch_idx+=1ifword1[ch_idx]!=word2[ch_idx]iffound_diff==nothingfound_diff=ch_idxelsereturnnothingendendendreturnfound_diffendfunction main()filename=ARGS[1]# julia arrays are 1-indexed!input=split(read(filename,String))test_input=["abcde","fghij","klmno","pqrst","fguij","axcye","wvxyz"]println(inventory_management_diff(input))endmain()
Julia's a weird language...it's so close to python that I forget it does weird things like "Arrays are 1-indexed", and it spells None/null as nothing
I'm a Staff Software Engineer at Cisco. I specialize in Python and Go, building functional, practical, and maintainable web systems leveraging Kubernetes and the cloud. Blog opinions are my own.
I'm a Staff Software Engineer at Cisco. I specialize in Python and Go, building functional, practical, and maintainable web systems leveraging Kubernetes and the cloud. Blog opinions are my own.
I was kind of hoping that the magical resizing was an automated resizing thing that happens and not one of you guys fixing it for me. I can actually put a background on it and scale it. What are the optimal dimensions? Is the white text on black ok or should I come up with something more fancy? I have very little aesthetic skill, so I’d appreciate any suggestions you or anyone else has.
Ryan, do you want me to design something? The only hard part is that I probably won't be up at midnight most nights to add specifics. Do you have any design software? If so, I can transfer you a file! We could also use Canva.
I'm a Staff Software Engineer at Cisco. I specialize in Python and Go, building functional, practical, and maintainable web systems leveraging Kubernetes and the cloud. Blog opinions are my own.
That would be amazing! I have a Figma account, but that’s it. I’ve never heard of Canva and pretty sure I don’t have any design software, but if you tell me the best way to handle it, I’ll happily do whatever you suggest. I’m happy to learn.
Python solutions!
part 1
part 2 -- this one feels clunky to me.
My part one ended up looking super similar!
I ended up using
zipto 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
Itertoolscrate and thetuple_combinationsmethod. Check out my updated solution in the thread.Itertoolsmakes iterators even more powerful.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
Clojure (inefficient part 2)
Part 1:
Part 2:
I like the threaded use of
updatehere in part 1 - my method used a transient map and returned a persistent copy at the end:Nice one. Is definitely faster than mine.
Enjoyed this one. My Kotlin solution:
Were you also annoyed that Kotlin has
.groupBybut not.frequencies?Have you thought about looking into
Sequence? You could make your pairs function lazily? UsingListmeans you're materializing the entirety of your double for-loop right away.The lack of
frequenciesdidn'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.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. :)
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
forloop 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.
PHP
Ended up learning about a bunch of useful array functions like
array_values,array_count_valuesandarray_diff_assocfor this one!Part 1:
Part 2:
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 😄
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!
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:
Part 1: C# + LINQ = one-liner
Part 2
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
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 😎
I am using Advent of Code to learn Golang, and here is the solution I came up with. Suggestions for improvements are always welcome!
Part 1:
Part 2:
My idea was to use a dictionary and store the subnames and see if we encounter one we have already visited. Since there should only be one match we can immediately return it. I learned a lot about using maps in Golang!
I am also using Python that I have more experience with to cross check solutions. I have tried to implement it with readability in mind, not performance.
Part 1:
Part 2:
I lost about 10 minutes to my son stalling getting into bed.
Kotlin Solution
Answer 1
Once again a fairly straightforward opener. Just run through, do some simple frequency checking and spit back the results. I think this is technically O(n2) but it moved fast enough for me. (And in a more lazy language, it ends up being closer to O(n) anyway)
Answer 2
As Ryan said, this is just Hamming distance with the added wrinkle that you need to throw away the differences while counting them. Lots of optimization room here, but once again, we shave off just under half the possibilities by only doing unique combinations and going from a raw
n^2to(n*(n+1))/2.At around 10 ms (calculated by shuffling my input 1000 times), I don't think there's an easy way to make this noticeably faster at this point without a more esoteric method.
Part 1 in Ruby:
I enjoyed playing around with the one liner
which produces a frequency chart something like this:
i.e. there are exactly 2 occurrences of the letter s in
strPart 2 in Ruby
Not happy about the double loop through the input file... but also can't think of a way to avoid it! It occurred to me that sorting the list first might improve the chances of finding a match earlier in the loop since all similar strings would be together, but thinking about it, perhaps there's equal chance that the similar strings would end up being sorted to the bottom of the list and it wouldn't help at all :/
Another thought - many of the input strings are v. similar, maybe they could be grouped into sets early on (e.g. based on the first 4 chars or something) and then you only look for similar strings in the same set?
Going to read into hamming distances now!
My solutions for part 1
Python
Go
Benchmark difference
Rust
Part 1
Part 2
I'm still searching for a nice iterator adapter to replace the nested loop.I finally used theitertoolscrate and it's AMAZING!!!Node.js
Common async generator. Read the file in chunk by chunk and yield each product ID based on new lines.
Part 1 was fun with some ES6 array -> Set -> Map to get the value counts
Part 2 got interesting. I needed to generate all pairs for every product ID. I made my Hamming Distance function also return the common letters. Then tied it all together by running each pair through the Hamming Distance function and getting the lowest.
Putting it all together:
Full code here: github.com/MattMorgis/Advent-Of-Co...
Making hamming distance return common letters is a slick way to go. I was looking at the duplication between the hamming distance and common letters functionality in my solution and was a little bummed about it, but I couldn't figure out a good way to do it.
I like this!
I didn't see any C# glancing through:
(Note
AddIfNotNullis from my extension methods package nuget.org/packages/Kritner.Extensi...Part 2:
I dunno how I'm gonna keep up during the week, but putting my solutions in the repo github.com/Kritner/Kritner.AdventO...
Giving it a go in good ol' JavaScript.
Part 1
First creates an Object to track the # of times a letter appears in a string. That gets converted to a Set to filter out any duplicate values to account for situations where 2 or more letters appeared twice (as the string only gets counted once for the check sum).
Part 2
I struggled with this one, so I'm sure there's a cleaner/more efficient way to do this. This takes each line of the input and compares it to the other lines, checking the characters and tracking the # of differences and the position of the last accounted for difference. The loops are set to break when a result with only 1 difference is found to prevent unnecessary looping.
My solution to day 2, in Elixir. The double for-loop in part 2 is certainly not optimal, but it works. The available time was short today. :)
Part one:
Part two:
Common:
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
linesfunction 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
frequenciesfunction, which is why I usedgroup-byfollowed bycount. But the 2filteroperations in the final calculation meant that the results of themapget 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
someoperation. This does a predicate test and terminates processing early, which is what I was doing manually with the innerloop.Also, my
closefunction 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%1to 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. 😊
Late to the party!
Still digging F#, but I'm definitely hindered by my lack of familiarity with what's available in .NET
My solution in JavaScript / Node 11, using the
readlineinterface:readLines.js
02a.js
02b.js
I'm not super thrilled with my Day 2 code, but I haven't really had the time to tweak it with everything going on at work currently.
Swift Solutions
Part 1
This one was fairly simple. Just count how many times each letter appears in each
Stringand act appropriately. I do like the fact that a SwiftStringtype is really anArrayofCharacters.Part 2
This one feels clunky, if I get a chance I'll revisit it.
I break on the first hit for a solution to short circuit everything and return the answer, this can help a lot with so many
Charactersto test.I use
zip(_:_:)with a filter and count to quickly test how many differences there are in the same positions. When I see two strings that differ by one character in the same position I move to the next step.In the second part I cast the Arrays into
Settypes so that I can use the Collection Algebra features to quickly find the actual character to remove by subtracting one collection from the other. With that done I can just remove it from the sourceArrayand return what's left.Normally I would
import Foundationhere so that I could just useNSOrderedSetand skip a few steps. I wanted to try and keep it all in the Swift Standard Library though, so I didn't!A bit late to the party, here are my solutions on Ruby:
Part 1:
Part 2:
I learnt about the combination method :)
Here's my C# .Net solution:
This one was difficult for me, but I eventually got it! I'm also not happy about that double for loop in part 2, but I think I sped it up by removing the elements as I compared them?
github.com/stevieoberg/advent-of-c...
Hosting my solutions on my github...
Code for https://adventofcode.com/2018/
advent-of-code-2018
Code for adventofcode.com/2018/
I'm trying to learn Julia so I'm using AoC to force myself to learn.
I'm pretty sure part 2 has to be O(n2) but you can cut down on the total number of iterations by only looking forward...here's my solution (implemented in Julia)
Julia's a weird language...it's so close to python that I forget it does weird things like "Arrays are 1-indexed", and it spells
None/nullasnothingI agree that Julia is weird, but I actually love it because it adds some functional stuff that I miss in python like the pipe operator!
We need better cover images 😂
I was kind of hoping that the magical resizing was an automated resizing thing that happens and not one of you guys fixing it for me. I can actually put a background on it and scale it. What are the optimal dimensions? Is the white text on black ok or should I come up with something more fancy? I have very little aesthetic skill, so I’d appreciate any suggestions you or anyone else has.
Ryan, do you want me to design something? The only hard part is that I probably won't be up at midnight most nights to add specifics. Do you have any design software? If so, I can transfer you a file! We could also use Canva.
That would be amazing! I have a Figma account, but that’s it. I’ve never heard of Canva and pretty sure I don’t have any design software, but if you tell me the best way to handle it, I’ll happily do whatever you suggest. I’m happy to learn.
Cool -- sending you a DM via /connect!