<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Jeff Keen</title>
  <subtitle>One guy with a website</subtitle>
  <id>https://jeffkeen.com/</id>
  <link href="https://jeffkeen.com/"/>
  <link href="https://jeffkeen.com/feed.xml" rel="self"/>
  <updated>2022-01-17T05:00:00-06:00</updated>
  <author>
    <name>Jeff Keen</name>
  </author>
  <entry>
    <title>This time it's different</title>
    <link rel="alternate" href="https://jeffkeen.com/p/this-time-its-different/"/>
    <id>https://jeffkeen.com/p/this-time-its-different/</id>
    <published>2022-01-17T05:00:00-06:00</published>
    <updated>2022-01-17T05:00:00-06:00</updated>
    <author>
      <name>Jeff Keen</name>
    </author>
    <summary type="html">&lt;p&gt;Recording an album in an actual recording studio is on my bucket list. I want some songs I wrote on vinyl and to be out in the world far more than I want to be on a stage. And I think this is the year that I do that.&lt;/p&gt;

&lt;p&gt;After nearly a two year songwriting slump I’m back in it, and have started organizing songs I have, re-working things that need re-working, and picking out the best ones to put on an album.&lt;/p&gt;

&lt;figure class=""&gt;
    &lt;img src="/images/music/pegboard.jpg" class="" style="aspect-ratio: 16/9" alt="" /&gt;
&lt;/figure&gt;

&lt;p&gt;This exercise has been reaffirming, and I’d like to share for accountability purposes the excuses I have told myself in the decade re: why I couldn’t record an album.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I don’t have enough songs - &lt;em&gt;lol&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;The songs aren’t good enough/ready - &lt;em&gt;do it anyway (and yeah, they are)&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;I should play the songs out in public first - &lt;em&gt;But why&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;I don’t have the money - &lt;em&gt;I spend more money on less important things&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Who would listen to it? - &lt;em&gt;doesn’t matter, me?&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;I need to get a band together first - &lt;em&gt;eh&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;I can probably record them well enough myself - &lt;em&gt;you absolute loon&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br /&gt;
This Time It’s Different&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Recording an album in an actual recording studio is on my bucket list. I want some songs I wrote on vinyl and to be out in the world far more than I want to be on a stage. And I think this is the year that I do that.&lt;/p&gt;

&lt;p&gt;After nearly a two year songwriting slump I’m back in it, and have started organizing songs I have, re-working things that need re-working, and picking out the best ones to put on an album.&lt;/p&gt;

&lt;figure class=""&gt;
    &lt;img src="/images/music/pegboard.jpg" class="" style="aspect-ratio: 16/9" alt="" /&gt;
&lt;/figure&gt;

&lt;p&gt;This exercise has been reaffirming, and I’d like to share for accountability purposes the excuses I have told myself in the decade re: why I couldn’t record an album.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I don’t have enough songs - &lt;em&gt;lol&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;The songs aren’t good enough/ready - &lt;em&gt;do it anyway (and yeah, they are)&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;I should play the songs out in public first - &lt;em&gt;But why&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;I don’t have the money - &lt;em&gt;I spend more money on less important things&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Who would listen to it? - &lt;em&gt;doesn’t matter, me?&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;I need to get a band together first - &lt;em&gt;eh&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;I can probably record them well enough myself - &lt;em&gt;you absolute loon&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br /&gt;
This Time It’s Different&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Fun Game</title>
    <link rel="alternate" href="https://jeffkeen.com/p/fun-game/"/>
    <id>https://jeffkeen.com/p/fun-game/</id>
    <published>2022-01-11T04:00:00-06:00</published>
    <updated>2022-01-11T04:00:00-06:00</updated>
    <author>
      <name>Jeff Keen</name>
    </author>
    <summary type="html">&lt;figure class="video float-right" style="aspect-ratio: 9/16;max-width: 300px"&gt;
  &lt;div class="video-wrapper"&gt;  
    &lt;iframe src="https://youtube.com/embed/9TZdp8duGDI?muted=true" frameborder="0" allowfullscreen=""&gt;&lt;/iframe&gt;
  &lt;/div&gt;
  &lt;figcaption&gt;Since recording this the memory got wiped on this calculator, so this is my only remaining evidence of Fun Game&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;In 1997, I was one of the few people in my high school who had a TI Graph Link, which was the little doohickey that connected the calculator to a computer’s serial port, allowing you to transfer programs from the computer to the calculator. This opened up a world of possibilities, letting me download all sorts of games from the internet which I would then distribute.&lt;/p&gt;

&lt;p&gt;My friend Matt and I somehow schemed this silly prank idea: create a game called “Fun Game” that we would transfer along with the programs the person actually wanted, and then wait.&lt;/p&gt;

&lt;p&gt;When it was launched, the whole sequence you see in the demo version would happen (which at the time we thought was hilarious), and then finally the program would copy a single file over and over and over &lt;em&gt;and over&lt;/em&gt; until the calculator ran out of memory.&lt;/p&gt;

&lt;p&gt;Turns out basic math functions take several seconds to perform after fun game ate all the memory. Fun, right?&lt;/p&gt;

</summary>
    <content type="html">&lt;figure class="video float-right" style="aspect-ratio: 9/16;max-width: 300px"&gt;
  &lt;div class="video-wrapper"&gt;  
    &lt;iframe src="https://youtube.com/embed/9TZdp8duGDI?muted=true" frameborder="0" allowfullscreen=""&gt;&lt;/iframe&gt;
  &lt;/div&gt;
  &lt;figcaption&gt;Since recording this the memory got wiped on this calculator, so this is my only remaining evidence of Fun Game&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;In 1997, I was one of the few people in my high school who had a TI Graph Link, which was the little doohickey that connected the calculator to a computer’s serial port, allowing you to transfer programs from the computer to the calculator. This opened up a world of possibilities, letting me download all sorts of games from the internet which I would then distribute.&lt;/p&gt;

&lt;p&gt;My friend Matt and I somehow schemed this silly prank idea: create a game called “Fun Game” that we would transfer along with the programs the person actually wanted, and then wait.&lt;/p&gt;

&lt;p&gt;When it was launched, the whole sequence you see in the demo version would happen (which at the time we thought was hilarious), and then finally the program would copy a single file over and over and over &lt;em&gt;and over&lt;/em&gt; until the calculator ran out of memory.&lt;/p&gt;

&lt;p&gt;Turns out basic math functions take several seconds to perform after fun game ate all the memory. Fun, right?&lt;/p&gt;

</content>
  </entry>
  <entry>
    <title>Bob Saget's Unsuccessful Music Career</title>
    <link rel="alternate" href="https://jeffkeen.com/p/bob-saget-bob-seger/"/>
    <id>https://jeffkeen.com/p/bob-saget-bob-seger/</id>
    <published>2022-01-09T05:00:00-06:00</published>
    <updated>2022-01-09T05:00:00-06:00</updated>
    <author>
      <name>Jeff Keen</name>
    </author>
    <summary type="html">&lt;figure class="float-right"&gt;
    &lt;img src="/images/blog/bob-seger-album.jpg" data-natural-width="488" data-natural-height="488" class="" style="aspect-ratio: 16/9" alt=""&gt;
&lt;/figure&gt;

&lt;p&gt;One night in 1989 while I was watching my favorite show (America’s Funniest Home Videos), my Dad explained to me that before Bob Saget was on TV, he had an unsuccessful musical career and in an effort to escape his past he had changed his name from “Bob Seger” to “Bob Saget”.&lt;/p&gt;

&lt;p&gt;7 or 8 years later, I’m 15 and flipping through a box of records at my friend’s house when I come across a Bob Seger album. Wouldn’t ya know it, I had a super fun trivia fact about Bob Seger! So I proudly shared it with the room.&lt;/p&gt;

&lt;p&gt;My friend’s dad laughed until he cried. At me, not with me.&lt;/p&gt;

&lt;p&gt;And so with that in mind, I say RIP Bob Saget&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</summary>
    <content type="html">&lt;figure class="float-right"&gt;
    &lt;img src="/images/blog/bob-seger-album.jpg" data-natural-width="488" data-natural-height="488" class="" style="aspect-ratio: 16/9" alt="" /&gt;
&lt;/figure&gt;

&lt;p&gt;One night in 1989 while I was watching my favorite show (America’s Funniest Home Videos), my Dad explained to me that before Bob Saget was on TV, he had an unsuccessful musical career and in an effort to escape his past he had changed his name from “Bob Seger” to “Bob Saget”.&lt;/p&gt;

&lt;p&gt;7 or 8 years later, I’m 15 and flipping through a box of records at my friend’s house when I come across a Bob Seger album. Wouldn’t ya know it, I had a super fun trivia fact about Bob Seger! So I proudly shared it with the room.&lt;/p&gt;

&lt;p&gt;My friend’s dad laughed until he cried. At me, not with me.&lt;/p&gt;

&lt;p&gt;And so with that in mind, I say RIP Bob Saget&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;figure class=""&gt;
    &lt;img src="/images/blog/rip-bob-saget.jpg" data-natural-width="900" data-natural-height="600" class="" style="aspect-ratio: 16/9" alt="" /&gt;
&lt;/figure&gt;

</content>
  </entry>
  <entry>
    <title>Big Bend National Park</title>
    <link rel="alternate" href="https://jeffkeen.com/lindawilson/big-bend"/>
    <id>https://jeffkeen.com/lindawilson/big-bend</id>
    <published>2022-01-02T11:34:16-06:00</published>
    <updated>2022-01-02T11:34:16-06:00</updated>
    <author>
      <name>Jeff Keen</name>
    </author>
    <summary type="html">&lt;p&gt;Dear Big Bend National Park,&lt;/p&gt;

&lt;p&gt;I recently had the pleasure of camping in your wonderful park and really enjoyed the natural beauty of the area. The Rio Grande river is exquisite and catching the sunrise in the valley with the light coming off the rock faces was absolutely breathtaking and a highlight of our trip. I found the trails to be somewhat challenging for a woman my age (but not impossible!) and my kids even behaved themselves and had a good time. Thank you for everything you do!&lt;/p&gt;

&lt;p&gt;However, I did have some trouble locating the clock tower. Not to discount the natural offerings of the area, but seeing one of the biggest clocks in the world would have been a nice addition to our amazing vacation. I assume the site has been closed for covid?&lt;/p&gt;

&lt;p&gt;Sincerely,&lt;br /&gt;
Linda B. Wilson&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Dear Big Bend National Park,&lt;/p&gt;

&lt;p&gt;I recently had the pleasure of camping in your wonderful park and really enjoyed the natural beauty of the area. The Rio Grande river is exquisite and catching the sunrise in the valley with the light coming off the rock faces was absolutely breathtaking and a highlight of our trip. I found the trails to be somewhat challenging for a woman my age (but not impossible!) and my kids even behaved themselves and had a good time. Thank you for everything you do!&lt;/p&gt;

&lt;p&gt;However, I did have some trouble locating the clock tower. Not to discount the natural offerings of the area, but seeing one of the biggest clocks in the world would have been a nice addition to our amazing vacation. I assume the site has been closed for covid?&lt;/p&gt;

&lt;p&gt;Sincerely,&lt;br /&gt;
Linda B. Wilson&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Ember Stereo</title>
    <link rel="alternate" href="https://jeffkeen.com/p/ember-stereo/"/>
    <id>https://jeffkeen.com/p/ember-stereo/</id>
    <published>2021-09-04T12:00:00-05:00</published>
    <updated>2021-09-04T12:00:00-05:00</updated>
    <author>
      <name>Jeff Keen</name>
    </author>
    <summary type="html">&lt;figure class="float-right"&gt;
    &lt;img src="/images/photos/instagram/13086824_10102396019205102_7520035177541596916_o_10102396019205102.jpg" data-natural-width="1080" data-natural-height="718" class="" style="aspect-ratio: 16/9" alt="" /&gt;
&lt;/figure&gt;

&lt;p&gt;Long ago I wrote an open source ember addon for New York Public Radio called &lt;a href="https://github.com/nypublicradio/ember-hifi"&gt;ember-hifi&lt;/a&gt; that powered their web audio applications, like &lt;a href="https://www.wqxr.org/"&gt;wnyc.org&lt;/a&gt;, &lt;a href="https://www.wqxr.org/"&gt;wqxr.org&lt;/a&gt;, &lt;a href="https://www.newsounds.org/"&gt;newsounds.org&lt;/a&gt;, &lt;a href="https://www.wnycstudios.org/"&gt;wnycstudios.org&lt;/a&gt;. It was used and maintained consistently for years and years, and as ember evolved it lagged behind a bit. So I started a branch to bring it up to modern Ember standards, and really started thinking about it from the outside in, to try and lower the learning curve.&lt;/p&gt;

&lt;p&gt;I find it tremendously useful to start with a interactive documentation site when building something like this, as the usability problems are easy to spot early. And building this one was no different. Naming things is the hardest part, and that stuff really jumps out at you when you’re trying to explain how to use it to someone else.&lt;/p&gt;

&lt;p&gt;After all the updates I did to the project, the changes got too big to feasibly do a mega-pull request to NYPR as their apps were still running legacy version of Ember and couldn’t benefit anyhow. So I did a hard fork, renamed it and launched it.&lt;/p&gt;
</summary>
    <content type="html">&lt;figure class="float-right"&gt;
    &lt;img src="/images/photos/instagram/13086824_10102396019205102_7520035177541596916_o_10102396019205102.jpg" data-natural-width="1080" data-natural-height="718" class="" style="aspect-ratio: 16/9" alt="" /&gt;
&lt;/figure&gt;

&lt;p&gt;Long ago I wrote an open source ember addon for New York Public Radio called &lt;a href="https://github.com/nypublicradio/ember-hifi"&gt;ember-hifi&lt;/a&gt; that powered their web audio applications, like &lt;a href="https://www.wqxr.org/"&gt;wnyc.org&lt;/a&gt;, &lt;a href="https://www.wqxr.org/"&gt;wqxr.org&lt;/a&gt;, &lt;a href="https://www.newsounds.org/"&gt;newsounds.org&lt;/a&gt;, &lt;a href="https://www.wnycstudios.org/"&gt;wnycstudios.org&lt;/a&gt;. It was used and maintained consistently for years and years, and as ember evolved it lagged behind a bit. So I started a branch to bring it up to modern Ember standards, and really started thinking about it from the outside in, to try and lower the learning curve.&lt;/p&gt;

&lt;p&gt;I find it tremendously useful to start with a interactive documentation site when building something like this, as the usability problems are easy to spot early. And building this one was no different. Naming things is the hardest part, and that stuff really jumps out at you when you’re trying to explain how to use it to someone else.&lt;/p&gt;

&lt;p&gt;After all the updates I did to the project, the changes got too big to feasibly do a mega-pull request to NYPR as their apps were still running legacy version of Ember and couldn’t benefit anyhow. So I did a hard fork, renamed it and launched it.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Spotify after signing Joe Rogan</title>
    <link rel="alternate" href="https://jeffkeen.com/p/spotify-after-signing-joe-rogan/"/>
    <id>https://jeffkeen.com/p/spotify-after-signing-joe-rogan/</id>
    <published>2021-08-23T06:00:00-05:00</published>
    <updated>2021-08-23T06:00:00-05:00</updated>
    <author>
      <name>Jeff Keen</name>
    </author>
    <summary type="html">&lt;figure class="video " style="aspect-ratio: 16/9"&gt;
  &lt;div class="video-wrapper"&gt;  
    &lt;iframe src="https://www.youtube.com/embed/82V4xbhZjC0" frameborder="0" allowfullscreen=""&gt;&lt;/iframe&gt;
  &lt;/div&gt;
  &lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I mean, seriously.&lt;/p&gt;
</summary>
    <content type="html">&lt;figure class="video " style="aspect-ratio: 16/9"&gt;
  &lt;div class="video-wrapper"&gt;  
    &lt;iframe src="https://www.youtube.com/embed/82V4xbhZjC0" frameborder="0" allowfullscreen=""&gt;&lt;/iframe&gt;
  &lt;/div&gt;
  &lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I mean, seriously.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Amazon Prime Jump Scares</title>
    <link rel="alternate" href="https://jeffkeen.com/p/amazon-prime-jumpscares/"/>
    <id>https://jeffkeen.com/p/amazon-prime-jumpscares/</id>
    <published>2021-06-18T01:00:00-05:00</published>
    <updated>2021-06-18T01:00:00-05:00</updated>
    <author>
      <name>Jeff Keen</name>
    </author>
    <summary type="html">&lt;p&gt;Amazon Prime started putting surprise advertising jump scares into movies that used to be completely free with a membership. This is the last thing I watched, and Prime just dropped to the last place I’ll check for movies.&lt;/p&gt;

&lt;figure class="video"&gt;
  &lt;div class="video-wrapper"&gt;
  &lt;video width="100%" height="auto" controls=""&gt;
    &lt;source src="/media/amazon-prime-jumpscares.mp4" type="video/mp4" /&gt;
    Aw man, no modern browser?
  &lt;/video&gt;
  &lt;/div&gt;
  &lt;figcaption&gt;Wait for it&lt;/figcaption&gt;
&lt;/figure&gt;
</summary>
    <content type="html">&lt;p&gt;Amazon Prime started putting surprise advertising jump scares into movies that used to be completely free with a membership. This is the last thing I watched, and Prime just dropped to the last place I’ll check for movies.&lt;/p&gt;

&lt;figure class="video"&gt;
  &lt;div class="video-wrapper"&gt;
  &lt;video width="100%" height="auto" controls=""&gt;
    &lt;source src="/media/amazon-prime-jumpscares.mp4" type="video/mp4" /&gt;
    Aw man, no modern browser?
  &lt;/video&gt;
  &lt;/div&gt;
  &lt;figcaption&gt;Wait for it&lt;/figcaption&gt;
&lt;/figure&gt;
</content>
  </entry>
  <entry>
    <title>Texas winter storm</title>
    <link rel="alternate" href="https://jeffkeen.com/p/texas-winter-storm/"/>
    <id>https://jeffkeen.com/p/texas-winter-storm/</id>
    <published>2021-05-01T01:00:00-05:00</published>
    <updated>2021-05-01T01:00:00-05:00</updated>
    <author>
      <name>Jeff Keen</name>
    </author>
    <summary type="html">&lt;p&gt;This may surprise you, but electricity is a pretty clutch utility, and not having it for three days in February during record below-freezing weather is a real challenge to say the least.&lt;/p&gt;

&lt;p&gt;I had been reading about &lt;a href="https://www.reddit.com/r/Austin/comments/lk7cgn/ercot_and_the_rolling_blackouts/"&gt;how stressed the grid was getting&lt;/a&gt; during the cold snap, and at 2am when the power went out I got up and hung up blankets over the windows to try and keep in the heat in case the power didn’t come back on for a while. Sealing them with plastic like people used to do in drafty houses would’ve been better, but cling wrap wasn’t gonna do it.&lt;/p&gt;

&lt;p&gt;Power didn’t come back on for three days. By the end of the power saga it was dipping below 40º in my house… and that is too cold.&lt;/p&gt;

&lt;p&gt;At first, it was kinda fun. I’ve been in Austin for thirteen years and I like the heat less and less each year (not to mention each year it seems to be getting worse), so any time we get any sort of inclement weather and the locals flip out I’m usually loving it. So getting a &lt;em&gt;real winter&lt;/em&gt; in this town? What a freakin’ dream.&lt;/p&gt;

&lt;p&gt;Losing power for days, though? No so much a dream. At the 48 hour mark we lost our damn minds, and just at the right moment my brother’s neighbor lent us a generator which powered a tiny heater, which was just enough heat to thaw out our patience.&lt;/p&gt;

&lt;figure class=""&gt;
    &lt;div style="position:relative;padding-bottom:57%"&gt;&lt;iframe src="https://gfycat.com/ifr/shadywarpedafghanhound" frameborder="0" scrolling="no" width="100%" height="100%" style="position:absolute;top:0;left:0;" allowfullscreen=""&gt;&lt;/iframe&gt;&lt;/div&gt;
    &lt;figcaption&gt;&lt;p&gt;This crew made the best of it, for sure&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;div class="photo-grid"&gt;
  &lt;div class="photo-grid-row"&gt;
    &lt;div class="photo-grid-item"&gt;
      &lt;figure class=""&gt;
    &lt;img src="/images/blog/freeze/forecast.png" data-natural-width="1125" data-natural-height="2436" class="" style="aspect-ratio: 16/9" alt=""&gt;
&lt;/figure&gt;

    &lt;/div&gt;
    &lt;div class="photo-grid-item"&gt;
      &lt;figure class=""&gt;
    &lt;img src="/images/blog/freeze/bundled.jpg" data-natural-width="3024" data-natural-height="4032" class="" style="aspect-ratio: 16/9" alt=""&gt;
&lt;/figure&gt;

    &lt;/div&gt;
    &lt;div class="photo-grid-item"&gt;
      &lt;figure class=""&gt;
    &lt;img src="/images/blog/freeze/ice-scraper.jpg" data-natural-width="1048" data-natural-height="699" class="" style="aspect-ratio: 16/9" alt=""&gt;
    &lt;figcaption&gt;&lt;p&gt;A rare item in this part of the world, we thought we could help out some neighbors with an ice scraper&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;

    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="photo-grid-row"&gt;
    &lt;div class="photo-grid-item"&gt;
      &lt;figure class=""&gt;
    &lt;img src="/images/blog/freeze/snowed-street-1.jpg" data-natural-width="1048" data-natural-height="699" class="" style="aspect-ratio: 16/9" alt=""&gt;
&lt;/figure&gt;

    &lt;/div&gt;
    &lt;div class="photo-grid-item"&gt;
      &lt;figure class=""&gt;
    &lt;img src="/images/blog/freeze/zoe-couch.jpg" data-natural-width="960" data-natural-height="1280" class="" style="aspect-ratio: 16/9" alt=""&gt;
&lt;/figure&gt;

    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="photo-grid-row"&gt;
    &lt;div class="photo-grid-item"&gt;
      &lt;figure class=""&gt;
    &lt;img src="/images/blog/freeze/snowed-street-2.jpg" data-natural-width="1048" data-natural-height="699" class="" style="aspect-ratio: 16/9" alt=""&gt;
&lt;/figure&gt;

    &lt;/div&gt;
    &lt;div class="photo-grid-item"&gt;
      &lt;figure class=""&gt;
    &lt;img src="/images/blog/freeze/summer-shandy.jpg" data-natural-width="3024" data-natural-height="4032" class="" style="aspect-ratio: 16/9" alt=""&gt;
    &lt;figcaption&gt;&lt;p&gt;This beer never tasted so good as it did when it was chilled outside in texas&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;

    &lt;/div&gt;
    &lt;div class="photo-grid-item"&gt;
      &lt;figure class=""&gt;
    &lt;img src="/images/blog/freeze/wonder-boys.jpg" data-natural-width="3024" data-natural-height="4032" class="" style="aspect-ratio: 16/9" alt=""&gt;
    &lt;figcaption&gt;&lt;p&gt;Macbook air with incredible battery life playing Wonder Boys from my ancient DVD collection.&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;

    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="photo-grid-row"&gt;
    &lt;div class="photo-grid-item"&gt;
      &lt;figure class=""&gt;
    &lt;img src="/images/blog/freeze/raccoon-reddit.png" data-natural-width="825" data-natural-height="314" class="" style="aspect-ratio: 16/9" alt=""&gt;
&lt;/figure&gt;

    &lt;/div&gt;
        &lt;div class="photo-grid-item"&gt;
      &lt;figure class=""&gt;
    &lt;img src="/images/blog/freeze/tiny-heater.jpg" data-natural-width="3024" data-natural-height="4032" class="" style="aspect-ratio: 16/9" alt=""&gt;
&lt;/figure&gt;

    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;</summary>
    <content type="html">&lt;p&gt;This may surprise you, but electricity is a pretty clutch utility, and not having it for three days in February during record below-freezing weather is a real challenge to say the least.&lt;/p&gt;

&lt;p&gt;I had been reading about &lt;a href="https://www.reddit.com/r/Austin/comments/lk7cgn/ercot_and_the_rolling_blackouts/"&gt;how stressed the grid was getting&lt;/a&gt; during the cold snap, and at 2am when the power went out I got up and hung up blankets over the windows to try and keep in the heat in case the power didn’t come back on for a while. Sealing them with plastic like people used to do in drafty houses would’ve been better, but cling wrap wasn’t gonna do it.&lt;/p&gt;

&lt;p&gt;Power didn’t come back on for three days. By the end of the power saga it was dipping below 40º in my house… and that is too cold.&lt;/p&gt;

&lt;p&gt;At first, it was kinda fun. I’ve been in Austin for thirteen years and I like the heat less and less each year (not to mention each year it seems to be getting worse), so any time we get any sort of inclement weather and the locals flip out I’m usually loving it. So getting a &lt;em&gt;real winter&lt;/em&gt; in this town? What a freakin’ dream.&lt;/p&gt;

&lt;p&gt;Losing power for days, though? No so much a dream. At the 48 hour mark we lost our damn minds, and just at the right moment my brother’s neighbor lent us a generator which powered a tiny heater, which was just enough heat to thaw out our patience.&lt;/p&gt;

&lt;figure class=""&gt;
    &lt;div style="position:relative;padding-bottom:57%"&gt;&lt;iframe src="https://gfycat.com/ifr/shadywarpedafghanhound" frameborder="0" scrolling="no" width="100%" height="100%" style="position:absolute;top:0;left:0;" allowfullscreen=""&gt;&lt;/iframe&gt;&lt;/div&gt;
    &lt;figcaption&gt;&lt;p&gt;This crew made the best of it, for sure&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;div class="photo-grid"&gt;
  &lt;div class="photo-grid-row"&gt;
    &lt;div class="photo-grid-item"&gt;
      &lt;figure class=""&gt;
    &lt;img src="/images/blog/freeze/forecast.png" data-natural-width="1125" data-natural-height="2436" class="" style="aspect-ratio: 16/9" alt="" /&gt;
&lt;/figure&gt;

    &lt;/div&gt;
    &lt;div class="photo-grid-item"&gt;
      &lt;figure class=""&gt;
    &lt;img src="/images/blog/freeze/bundled.jpg" data-natural-width="3024" data-natural-height="4032" class="" style="aspect-ratio: 16/9" alt="" /&gt;
&lt;/figure&gt;

    &lt;/div&gt;
    &lt;div class="photo-grid-item"&gt;
      &lt;figure class=""&gt;
    &lt;img src="/images/blog/freeze/ice-scraper.jpg" data-natural-width="1048" data-natural-height="699" class="" style="aspect-ratio: 16/9" alt="" /&gt;
    &lt;figcaption&gt;&lt;p&gt;A rare item in this part of the world, we thought we could help out some neighbors with an ice scraper&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;

    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="photo-grid-row"&gt;
    &lt;div class="photo-grid-item"&gt;
      &lt;figure class=""&gt;
    &lt;img src="/images/blog/freeze/snowed-street-1.jpg" data-natural-width="1048" data-natural-height="699" class="" style="aspect-ratio: 16/9" alt="" /&gt;
&lt;/figure&gt;

    &lt;/div&gt;
    &lt;div class="photo-grid-item"&gt;
      &lt;figure class=""&gt;
    &lt;img src="/images/blog/freeze/zoe-couch.jpg" data-natural-width="960" data-natural-height="1280" class="" style="aspect-ratio: 16/9" alt="" /&gt;
&lt;/figure&gt;

    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="photo-grid-row"&gt;
    &lt;div class="photo-grid-item"&gt;
      &lt;figure class=""&gt;
    &lt;img src="/images/blog/freeze/snowed-street-2.jpg" data-natural-width="1048" data-natural-height="699" class="" style="aspect-ratio: 16/9" alt="" /&gt;
&lt;/figure&gt;

    &lt;/div&gt;
    &lt;div class="photo-grid-item"&gt;
      &lt;figure class=""&gt;
    &lt;img src="/images/blog/freeze/summer-shandy.jpg" data-natural-width="3024" data-natural-height="4032" class="" style="aspect-ratio: 16/9" alt="" /&gt;
    &lt;figcaption&gt;&lt;p&gt;This beer never tasted so good as it did when it was chilled outside in texas&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;

    &lt;/div&gt;
    &lt;div class="photo-grid-item"&gt;
      &lt;figure class=""&gt;
    &lt;img src="/images/blog/freeze/wonder-boys.jpg" data-natural-width="3024" data-natural-height="4032" class="" style="aspect-ratio: 16/9" alt="" /&gt;
    &lt;figcaption&gt;&lt;p&gt;Macbook air with incredible battery life playing Wonder Boys from my ancient DVD collection.&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;

    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="photo-grid-row"&gt;
    &lt;div class="photo-grid-item"&gt;
      &lt;figure class=""&gt;
    &lt;img src="/images/blog/freeze/raccoon-reddit.png" data-natural-width="825" data-natural-height="314" class="" style="aspect-ratio: 16/9" alt="" /&gt;
&lt;/figure&gt;

    &lt;/div&gt;
        &lt;div class="photo-grid-item"&gt;
      &lt;figure class=""&gt;
    &lt;img src="/images/blog/freeze/tiny-heater.jpg" data-natural-width="3024" data-natural-height="4032" class="" style="aspect-ratio: 16/9" alt="" /&gt;
&lt;/figure&gt;

    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Power finally came on almost exactly 72 hours after it went off, and it stayed on for &lt;em&gt;daaaayys&lt;/em&gt;. And then it went off for hours, on for days, off for hours, on for days, off for hours, etc. What a blast.&lt;/p&gt;

&lt;p&gt;I attended a zoom conference with my out-of-patience neighbors, our nearly useless district city council rep, and Austin Energy where the situation was &lt;em&gt;managed&lt;/em&gt;. Austin Energy took no responsibility for it and claimed that all the power outages were due to circumstances out of their control—historic weather events, trees crowding the power lines, and the best one: three different different occassions of someone crashing into a utility pole, &lt;em&gt;in our neighborhood&lt;/em&gt;. I find that really hard to believe.&lt;/p&gt;

&lt;p&gt;But anyway, let’s see what happens next year come winter.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>The story of the thong song</title>
    <link rel="alternate" href="https://jeffkeen.com/p/story-of-the-thong-song/"/>
    <id>https://jeffkeen.com/p/story-of-the-thong-song/</id>
    <published>2021-02-23T00:00:00-06:00</published>
    <updated>2021-02-23T00:00:00-06:00</updated>
    <author>
      <name>Jeff Keen</name>
    </author>
    <summary type="html">&lt;p&gt;This was surprisingly fascinating. This song’s roots are… Eleanor Rigby??&lt;/p&gt;

&lt;figure class="video " style="aspect-ratio: 16/9"&gt;
  &lt;div class="video-wrapper"&gt;  
    &lt;iframe src="https://youtube.com/embed/Y0S1buCBwGI" frameborder="0" allowfullscreen=""&gt;&lt;/iframe&gt;
  &lt;/div&gt;
  &lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

</summary>
    <content type="html">&lt;p&gt;This was surprisingly fascinating. This song’s roots are… Eleanor Rigby??&lt;/p&gt;

&lt;figure class="video " style="aspect-ratio: 16/9"&gt;
  &lt;div class="video-wrapper"&gt;  
    &lt;iframe src="https://youtube.com/embed/Y0S1buCBwGI" frameborder="0" allowfullscreen=""&gt;&lt;/iframe&gt;
  &lt;/div&gt;
  &lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

</content>
  </entry>
  <entry>
    <title>The only spin doctors shorts in existence?</title>
    <link rel="alternate" href="https://jeffkeen.com/p/spin-doctors-shorts/"/>
    <id>https://jeffkeen.com/p/spin-doctors-shorts/</id>
    <published>2021-01-20T00:00:00-06:00</published>
    <updated>2021-01-20T00:00:00-06:00</updated>
    <author>
      <name>Jeff Keen</name>
    </author>
    <summary type="html">&lt;figure class="float-right"&gt;
    &lt;img src="https://i.imgur.com/VIJ69og.jpg" class="" style="aspect-ratio: 16/9" alt="" /&gt;
&lt;/figure&gt;

&lt;p&gt;Spin Doctors shorts, designer unknown. Birkies and Shaka to complete the look.&lt;/p&gt;

&lt;p&gt;A friend gave these shorts to me in 2002 or so, and I have no idea where he got them from. Despite their appearance, they’re not made for water activities and I never wear them because they’re so heinous. But I’ve kept them all these years because they they feel like a precious artifact from another time and the mystery of how they got here is intriguing.&lt;/p&gt;

&lt;p&gt;Who green-lit this project? Did the designer intentionally make the pockets comically tiny as a tip of their hat to the album we all know? When I came down the stairs in these shorts was my girlfriend laughing at me or with me?&lt;/p&gt;
</summary>
    <content type="html">&lt;figure class="float-right"&gt;
    &lt;img src="https://i.imgur.com/VIJ69og.jpg" class="" style="aspect-ratio: 16/9" alt="" /&gt;
&lt;/figure&gt;

&lt;p&gt;Spin Doctors shorts, designer unknown. Birkies and Shaka to complete the look.&lt;/p&gt;

&lt;p&gt;A friend gave these shorts to me in 2002 or so, and I have no idea where he got them from. Despite their appearance, they’re not made for water activities and I never wear them because they’re so heinous. But I’ve kept them all these years because they they feel like a precious artifact from another time and the mystery of how they got here is intriguing.&lt;/p&gt;

&lt;p&gt;Who green-lit this project? Did the designer intentionally make the pockets comically tiny as a tip of their hat to the album we all know? When I came down the stairs in these shorts was my girlfriend laughing at me or with me?&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Crest Charcoal Toothpaste</title>
    <link rel="alternate" href="https://jeffkeen.com/lindawilson/crest-charcoal-toothpaste"/>
    <id>https://jeffkeen.com/lindawilson/crest-charcoal-toothpaste</id>
    <published>2020-01-21T11:34:16-06:00</published>
    <updated>2020-01-21T11:34:16-06:00</updated>
    <author>
      <name>Jeff Keen</name>
    </author>
    <summary type="html">&lt;p&gt;Dear Crest,&lt;/p&gt;

&lt;p&gt;I recently saw a video on youtube about whitening your teeth using charcoal, and your charcoal toothpaste was one of the recommended brands.&lt;/p&gt;

&lt;p&gt;My son has very sensitive teeth, and the Kingsford charcoal “briquettes” I have been using to clean his teeth have not been working as I had expected. He complains of the taste, and his teeth seem to be blacker than they were before. Does your charcoal toothpaste suffer from the same problem?&lt;/p&gt;

&lt;p&gt;I am open to switching brands, but we need to get through this bag first. My husband is having a grill out this weekend, so we might be able to switch soon.&lt;/p&gt;

&lt;p&gt;Linda&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Dear Crest,&lt;/p&gt;

&lt;p&gt;I recently saw a video on youtube about whitening your teeth using charcoal, and your charcoal toothpaste was one of the recommended brands.&lt;/p&gt;

&lt;p&gt;My son has very sensitive teeth, and the Kingsford charcoal “briquettes” I have been using to clean his teeth have not been working as I had expected. He complains of the taste, and his teeth seem to be blacker than they were before. Does your charcoal toothpaste suffer from the same problem?&lt;/p&gt;

&lt;p&gt;I am open to switching brands, but we need to get through this bag first. My husband is having a grill out this weekend, so we might be able to switch soon.&lt;/p&gt;

&lt;p&gt;Linda&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>How to Correct 32,000 Incorrect CSV Files in Fewer Than 32,000 Steps</title>
    <link rel="alternate" href="https://jeffkeen.com/p/how-to-correct-32000-incorrect-csv-files/"/>
    <id>https://jeffkeen.com/p/how-to-correct-32000-incorrect-csv-files/</id>
    <published>2019-08-07T00:00:00-05:00</published>
    <updated>2019-08-07T00:00:00-05:00</updated>
    <author>
      <name>Jeff Keen</name>
    </author>
    <summary type="html">&lt;figure class=""&gt;
    &lt;img src="/images/comma-splice/mess.png" data-natural-width="700" data-natural-height="357" class="" style="aspect-ratio: 16/9" alt=""&gt;
    &lt;figcaption&gt;&lt;p&gt;Quick, find the extra comma!&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;CSV feels like the simplest of file formats, where it might seem that there’s not much to know after mentally expanding the acronym - Comma Separated Values. But tell me this: if it’s so simple, then why are there so many CSV parsing libraries, alternative CSV parsing libraries, and CSV parsing libraries that claim to be better or smarter, and a mountain of mangled CSVs in existence?&lt;/p&gt;

&lt;p&gt;CSV isn’t so much a file format as it is a loose set of guidelines for converting tabular data into text. The closest thing to a spec for it is this, which deals with vital and often overlooked questions such as:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;“What happens if a value has a comma in it?” - oh, you quote it&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;“What happens if a value has a quote in it?” - oh, you put another quote before it&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One question the spec definitely does not cover is one I needed answering: “What do you do with 32,000 files claiming to be valid CSVs but of the 750,000 some lines an unknown number of them have extra unquoted commas hidden in the values, basically making the data untrustworthy?”
This is not such a simple problem, but it’s an interesting problem.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</summary>
    <content type="html">&lt;figure class=""&gt;
    &lt;img src="/images/comma-splice/mess.png" data-natural-width="700" data-natural-height="357" class="" style="aspect-ratio: 16/9" alt="" /&gt;
    &lt;figcaption&gt;&lt;p&gt;Quick, find the extra comma!&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;CSV feels like the simplest of file formats, where it might seem that there’s not much to know after mentally expanding the acronym - Comma Separated Values. But tell me this: if it’s so simple, then why are there so many CSV parsing libraries, alternative CSV parsing libraries, and CSV parsing libraries that claim to be better or smarter, and a mountain of mangled CSVs in existence?&lt;/p&gt;

&lt;p&gt;CSV isn’t so much a file format as it is a loose set of guidelines for converting tabular data into text. The closest thing to a spec for it is this, which deals with vital and often overlooked questions such as:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;“What happens if a value has a comma in it?” - oh, you quote it&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;“What happens if a value has a quote in it?” - oh, you put another quote before it&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One question the spec definitely does not cover is one I needed answering: “What do you do with 32,000 files claiming to be valid CSVs but of the 750,000 some lines an unknown number of them have extra unquoted commas hidden in the values, basically making the data untrustworthy?”
This is not such a simple problem, but it’s an interesting problem.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;I’ve been building a platform for public and community radio stations the last year and have been working with the fantastic 91.7 KOOP in Austin, TX to pilot it. Part of this project involved exporting the data from their old system (some 750,000 tracks) into the system I built for them. And noticing some tracks from an artist named “10” doing a song called “000 maniacs”, I realized the old system’s crusty CSV exporter did not do the right thing when it came to commas in values, leaving me with a real mess.&lt;/p&gt;

&lt;p&gt;How many track titles, artists, albums, or record labels have commas in their names? Many.&lt;/p&gt;

&lt;p&gt;I initially thought this was an impossible problem to solve (at least in a way that would not drive a person insane) so I tried reaching out to the developer of the old system to fix their ancient CSV exporter, or to send me data in another format that I could sort through myself. This felt like the most straightforward option and didn’t seem like too much of a lift.
But after weeks and weeks of back and forth, this path dead ended leaving me with no other option but to fix it myself.&lt;/p&gt;

&lt;h2&gt;The Problem&lt;/h2&gt;

&lt;p&gt;Here’s an abbreviated and simplified example of a messed up CSV I was dealing with:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;artist&lt;/th&gt;
      &lt;th&gt;title&lt;/th&gt;
      &lt;th&gt;album&lt;/th&gt;
      &lt;th&gt;label&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Lester Sterling&lt;/td&gt;
      &lt;td&gt;Lynn Taitt &amp;amp; The Jets&lt;/td&gt;
      &lt;td&gt;Check Point Charlie&lt;/td&gt;
      &lt;td&gt;Merritone Rock Steady 3: Bang Bang Rock Steady 1966-1968&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Lester Sterling&lt;/td&gt;
      &lt;td&gt;Lester Sterling Special&lt;/td&gt;
      &lt;td&gt;Merritone Rock Steady 2: This Music Got Soul 1966-1967&lt;/td&gt;
      &lt;td&gt;Dub Store&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The way a CSV parser would parse that data currently is like this:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;artist&lt;/th&gt;
      &lt;th&gt;title&lt;/th&gt;
      &lt;th&gt;album&lt;/th&gt;
      &lt;th&gt;label&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Lester Sterling&lt;/td&gt;
      &lt;td&gt;Lynn Taitt &amp;amp; The Jets&lt;/td&gt;
      &lt;td&gt;Check Point Charlie&lt;/td&gt;
      &lt;td&gt;Merritone Rock Steady 3: Bang Bang Rock Steady 1966-1968&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Lester Sterling&lt;/td&gt;
      &lt;td&gt;Lester Sterling Special&lt;/td&gt;
      &lt;td&gt;Merritone Rock Steady 2: This Music Got Soul 1966-1967&lt;/td&gt;
      &lt;td&gt;Dub Store&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;This is incorrect, and would only be parsed correctly if the artist value were properly quoted, like so:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&lt;table class="rouge-table"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="rouge-gutter gl"&gt;&lt;pre class="lineno"&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class="rouge-code"&gt;&lt;pre&gt;artist,title,album,label
"Lester Sterling, Lynn Taitt &amp;amp; The Jets",Check Point Charlie,Merritone Rock Steady 3: Bang Bang Rock Steady 1966–1968,Dub Store,
Lester Sterling,Lester Sterling Special,Merritone Rock Steady 2: This Music Got Soul 1966–1967,Dub Store,
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Which would be parsed like this:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;artist&lt;/th&gt;
      &lt;th&gt;title&lt;/th&gt;
      &lt;th&gt;album&lt;/th&gt;
      &lt;th&gt;label&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Lester Sterling, Lynn Taitt &amp;amp; The Jets&lt;/td&gt;
      &lt;td&gt;Check Point Charlie&lt;/td&gt;
      &lt;td&gt;Merritone Rock Steady 3: Bang Bang Rock Steady 1966-1968&lt;/td&gt;
      &lt;td&gt;Dub Store&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Lester Sterling&lt;/td&gt;
      &lt;td&gt;Lester Sterling Special&lt;/td&gt;
      &lt;td&gt;Merritone Rock Steady 2: This Music Got Soul 1966-1967&lt;/td&gt;
      &lt;td&gt;Dub Store&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;But how do we get there without manually looking at 750,000 lines of comma separated text?&lt;/p&gt;

&lt;h2&gt;Computers, man&lt;/h2&gt;
&lt;p&gt;First, we can determine if a line is incorrect by parsing it individually as a CSV, seeing how many values we end up with, and comparing that number with the number of headers.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;table class="rouge-table"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="rouge-gutter gl"&gt;&lt;pre class="lineno"&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class="rouge-code"&gt;&lt;pre&gt;&lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/path/to/file.csv'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;lines&lt;/span&gt;
&lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;CSV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;liberal_parsing: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;incorrect_lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="no"&gt;CSV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;liberal_parsing: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;table class="rouge-table"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="rouge-gutter gl"&gt;&lt;pre class="lineno"&gt;1
2
3
4
5
6
7
8
9
10
11
12
&lt;/pre&gt;&lt;/td&gt;&lt;td class="rouge-code"&gt;&lt;pre&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"artist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"album"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"label"&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"Lester Sterling"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;" Lynn Taitt &amp;amp; The Jets"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Check Point Charlie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Merritone Rock Steady 3: Bang Bang Rock Steady 1966-1968"&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Dub Store"&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"Lester Sterling"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Lester Sterling Special"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Merritone Rock Steady 2: This Music Got Soul 1966-1967"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Dub Store"&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For this example, the header row has 4 values, the first line has 5 values, and the second line has 4 values. The first line is incorrect, meaning there’s an extra comma that belongs within a value.&lt;/p&gt;

&lt;p&gt;Knowing that, we need to figure out all the different possibilities of what it could be. One of those possibilities will be the correct one, and the rest will be wrong. With five values and four fields, we need to join two values together into one (and quote it), making four values.&lt;/p&gt;

&lt;p&gt;I think best on paper, so I started drawing out how this might work in a brute force sort of way, to try to get a handle on the algorithm I needed.&lt;/p&gt;

&lt;h3&gt;Eliminate 1 position, Fit 5 into 4&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;[1, 2, 3, 4, 5] -&amp;gt; [x, x, x, x] =&amp;gt;&lt;/p&gt;

  &lt;p&gt;[[1, 2], 3, 4, 5]&lt;/p&gt;

  &lt;p&gt;[1, [2, 3], 4, 5]&lt;/p&gt;

  &lt;p&gt;[1, 2, [3, 4], 5]&lt;/p&gt;

  &lt;p&gt;[1, 2, 3, [4, 5]]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What if we had two commas in there? Then we’d have six values needing to fit into four slots.&lt;/p&gt;

&lt;h3&gt;Eliminate 2 positions, Fit 6 into 4&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;[1, 2, 3, 4, 5, 6] -&amp;gt; [x, x, x, x] =&amp;gt;&lt;/p&gt;

  &lt;p&gt;[[1, 2, 3], 4, 5, 6]&lt;/p&gt;

  &lt;p&gt;[1, [2, 3, 4], 5, 6]&lt;/p&gt;

  &lt;p&gt;[1, 2, [3, 4, 5], 6]&lt;/p&gt;

  &lt;p&gt;[1, 2, 3, [4, 5, 6]]&lt;/p&gt;

  &lt;p&gt;[[1, 2], [3, 4], 5, 6]&lt;/p&gt;

  &lt;p&gt;[[1, 2], 3, [4, 5], 6]&lt;/p&gt;

  &lt;p&gt;[[1, 2], 3, 4, [5, 6]]&lt;/p&gt;

  &lt;p&gt;[1, [2, 3], [4, 5], 6]&lt;/p&gt;

  &lt;p&gt;[1, [2, 3], 4, [5, 6]]&lt;/p&gt;

  &lt;p&gt;[1, 2, [3, 4], [5, 6]]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This was pretty easy to visualize and draw out on paper manually, but figuring out how to do this programmatically for any situation was a challenge. This sort of combination/permutation type math felt like the type of thing I would have learned in a Probability and Statistics class that I took in college, but you know what I remember from that class?&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Having a huge crush on a lil’ cutie and zero game to do anything about it&lt;/li&gt;
  &lt;li&gt;Drawing this funny comic&lt;/li&gt;
&lt;/ol&gt;

&lt;figure class=""&gt;
    &lt;img src="/images/comma-splice/stats-comic.jpeg" data-natural-width="700" data-natural-height="484" class="" style="aspect-ratio: 16/9" alt="" /&gt;
    &lt;figcaption&gt;&lt;p&gt;This was less dark in 2002&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;That’s what I remember. Certainly not this practical math.&lt;/p&gt;

&lt;h2&gt;The Actual Question&lt;/h2&gt;

&lt;p&gt;Anyway, turns out the question we’re trying to answer is: if v is the number of values we have (5), and h is the number of headers we have (4), what are all the unique permutations of h numbers that add up to v?&lt;/p&gt;

&lt;p&gt;The following looks similar to the written out paper breakdowns from before, but now instead of each number representing a position, each number represents how many values will be joined together.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What are all the permutations of 4 numbers that add up to 5?&lt;/p&gt;

  &lt;p&gt;[2, 1, 1, 1] # join the first two values together&lt;/p&gt;

  &lt;p&gt;[1, 2, 1, 1] # join the second and third values together&lt;/p&gt;

  &lt;p&gt;[1, 1, 2, 1] # join the third and fourth values together&lt;/p&gt;

  &lt;p&gt;[1, 1, 1, 2] # join the fourth and fifth values together&lt;/p&gt;

&lt;/blockquote&gt;

&lt;p&gt;To calculate this programmatically, we can first calculate the unique combinations using the method below that I nicked from a stack overflow post that has since been lost in my browser history. There were a number of them, and this one seemed most performant.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;table class="rouge-table"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="rouge-gutter gl"&gt;&lt;pre class="lineno"&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
&lt;/pre&gt;&lt;/td&gt;&lt;td class="rouge-code"&gt;&lt;pre&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;combos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;desired_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minimum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;# determine all combinations of [count] numbers that add up to [desired_size]&lt;/span&gt;
  &lt;span class="c1"&gt;# e.g if we have an array of 6 items and want an array of 4 items&lt;/span&gt;
  &lt;span class="c1"&gt;# we need 4 numbers that add up to 6, =&amp;gt; [[1, 1, 1, 3], [1, 1, 2, 2]]&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;desired_size&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;desired_size&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;minimum&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;desired_size&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;minimum&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;desired_size&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;flat_map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;combos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;desired_size&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Given these combinations we can now calculate all the different permutations of those sets, which is basically just getting every ordering of those numbers and then eliminating duplicates.&lt;/p&gt;

&lt;p&gt;Here’s a class that does all this:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;table class="rouge-table"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="rouge-gutter gl"&gt;&lt;pre class="lineno"&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
&lt;/pre&gt;&lt;/td&gt;&lt;td class="rouge-code"&gt;&lt;pre&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;CommaSplice&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;JoinCalculator&lt;/span&gt;
    &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:from_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:to_size&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;header_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@from_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value_count&lt;/span&gt;
      &lt;span class="vi"&gt;@to_size&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;header_count&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;possibilities&lt;/span&gt;
      &lt;span class="vi"&gt;@possibilities&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="n"&gt;permutations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;combos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;from_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to_size&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="kp"&gt;private&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;permutations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;combinations&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="c1"&gt;# get all permutations of those combinations&lt;/span&gt;
      &lt;span class="c1"&gt;# to determine every possibility of join&lt;/span&gt;

      &lt;span class="n"&gt;all_permutations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;combinations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;combo&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;combo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;permutation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to_size&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="c1"&gt;# flatten down to a list of arrays&lt;/span&gt;
      &lt;span class="n"&gt;all_permutations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;uniq&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;combos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;desired_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minimum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="c1"&gt;# determine all combinations of [count] numbers that add up to [desired_size]&lt;/span&gt;
      &lt;span class="c1"&gt;# e.g if we have an array of 6 items and want an array of 4 items&lt;/span&gt;
      &lt;span class="c1"&gt;# we need 4 numbers that add up to 6, =&amp;gt; [[1, 1, 1, 3], [1, 1, 2, 2]]&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;desired_size&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;desired_size&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;minimum&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;desired_size&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;minimum&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;desired_size&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;flat_map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;combos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;desired_size&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# CommaSplice::JoinCalculator.new(6, 4).possibilities&lt;/span&gt;
&lt;span class="c1"&gt;#  #=&amp;gt; [[1,1,1,2], [1,1,2,1], [1,2,1,1], [2,1,1,1]]`&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now given all the permutation options, we can loop through them and generate all the value possibilities.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;table class="rouge-table"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="rouge-gutter gl"&gt;&lt;pre class="lineno"&gt;1
2
3
4
5
6
7
8
9
10
11
&lt;/pre&gt;&lt;/td&gt;&lt;td class="rouge-code"&gt;&lt;pre&gt;&lt;span class="n"&gt;join_possibilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;joins&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dup&lt;/span&gt;
  &lt;span class="n"&gt;joins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;join_num&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;join_num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="n"&gt;quote_values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here are the value possibilities. Five values made into four.&lt;/p&gt;

&lt;p&gt;At this point, we could match these up with the headers and prompt the user with the option, making the task a little less tedious, like so:&lt;/p&gt;

&lt;figure class=""&gt;
    &lt;img src="/images/comma-splice/comma-splice-prompt.png" data-natural-width="700" data-natural-height="389" class="" style="aspect-ratio: 16/9" alt="" /&gt;
&lt;/figure&gt;

&lt;p&gt;And that’s a pretty good worst case scenario! But there’s still something we can do to determine which one of these is most likely correct without dying from boredom after doing this thousands of times.&lt;/p&gt;

&lt;h2&gt;The Human Element&lt;/h2&gt;
&lt;p&gt;You might have already have caught this, and if so — nice work, you’re sharp — but this crucial fact only hit me when I was a mile deep into this problem: when people type commas out they generally put a space after it. So if a parsed value starts with a space…it’s probably not the correct choice.&lt;/p&gt;

&lt;p&gt;So in this example we can obviously see that option number 4 is the correct one, since every other one has a value starting with a space. So most of the time, we can reject any choice where any value starts with a space.&lt;/p&gt;

&lt;p&gt;This doesn’t always work! With 750,000 manually entered tracks, you can bet people made typos, and you can bet there are tracks that have commas without spaces after them, like this beauty:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Deftones,U,U,D,D,L,R,L,R,Select,Start,Saturday Night Wrist,Maverick&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(which, by the way, generates 220 possible options with no great way to determine which one is correct without a human and/or an internet connection. Oy vey.)&lt;/p&gt;

&lt;p&gt;But this method works well enough to turn what I initially thought to be an impossible task into a very very doable task, which I’m happy to report I completed successfully.&lt;/p&gt;

&lt;p&gt;I packaged all this code into a &lt;a href="https://github.com/jkeen/comma_splice"&gt;ruby gem&lt;/a&gt; (with a command line option) on the off chance that some other poor soul has found themselves in a similar CSV parsing dilemma. If that’s you: the chances of you getting out of that situation alive? Better than average.&lt;/p&gt;

</content>
  </entry>
  <entry>
    <title>Hawaiian Rumble Adventure Golf</title>
    <link rel="alternate" href="https://jeffkeen.com/lindawilson/mini-golf"/>
    <id>https://jeffkeen.com/lindawilson/mini-golf</id>
    <published>2019-06-21T12:34:16-05:00</published>
    <updated>2019-06-21T12:34:16-05:00</updated>
    <author>
      <name>Jeff Keen</name>
    </author>
    <summary type="html">&lt;p&gt;Dear Hawaiian Rumble Adventure Golf,&lt;/p&gt;

&lt;p&gt;My family played your mini golf course last weekend and loved it! It seems warn enough out so I’m wondering when the pool surrounding the course will be open for swimming? Also, is the water around the last hole very deep? It was too black to see clearly, but my son Aaron loves to dive and we’ve now learned that we need to check depths before doing so.&lt;/p&gt;

&lt;p&gt;What kind of pool toys do you provide, and do you have an air pump available for customer use? The inflatable white swan we have takes so long to blow up.&lt;/p&gt;

&lt;p&gt;Sincerely,&lt;br /&gt;
Linda B. Wilson&lt;/p&gt;

&lt;p&gt;P.S. How much of a discount do we get for each golf ball we retrieve from the bottom of the pool? Does the color matter?&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;Dear Hawaiian Rumble Adventure Golf,&lt;/p&gt;

&lt;p&gt;My family played your mini golf course last weekend and loved it! It seems warn enough out so I’m wondering when the pool surrounding the course will be open for swimming? Also, is the water around the last hole very deep? It was too black to see clearly, but my son Aaron loves to dive and we’ve now learned that we need to check depths before doing so.&lt;/p&gt;

&lt;p&gt;What kind of pool toys do you provide, and do you have an air pump available for customer use? The inflatable white swan we have takes so long to blow up.&lt;/p&gt;

&lt;p&gt;Sincerely,&lt;br /&gt;
Linda B. Wilson&lt;/p&gt;

&lt;p&gt;P.S. How much of a discount do we get for each golf ball we retrieve from the bottom of the pool? Does the color matter?&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Everything Is Not Lost</title>
    <link rel="alternate" href="https://jeffkeen.com/p/everything-is-not-lost/"/>
    <id>https://jeffkeen.com/p/everything-is-not-lost/</id>
    <published>2019-06-01T06:00:00-05:00</published>
    <updated>2019-06-01T06:00:00-05:00</updated>
    <author>
      <name>Jeff Keen</name>
    </author>
    <summary type="html">&lt;div class="song-metadata"&gt;
    &lt;span class="song-date"&gt;
      Written &amp;amp; Recorded: &lt;time&gt;Spring 2019&lt;/time&gt;
    &lt;/span&gt;
&lt;/div&gt;

&lt;p&gt;I’m pretty proud of this one. This was my first demo recording of it just to get it down, and I hadn’t yet written the bridge or the second half, but I think has the feeling I’m going for in it. Somber lyrics paired with a little skip in your step, ya know?&lt;/p&gt;

&lt;div class="soundcloud-player"&gt;

&lt;iframe width="100%" height="166" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/1198303366%3Fsecret_token%3Ds-B5YzeYS2rr3&amp;amp;auto_play=false&amp;amp;hide_related=true&amp;amp;show_comments=false&amp;amp;show_user=true&amp;amp;show_reposts=false&amp;amp;show_teaser=false"&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;This version is the full song, adding a bridge and another verse, but I think at this tempo with just a piano it sounds way too sad. Pretty, but sad.&lt;/p&gt;

&lt;div class="soundcloud-player"&gt;

&lt;iframe width="100%" height="166" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https://soundcloud.com/jeffkeen/everything-is-not-lost&amp;amp;auto_play=false&amp;amp;hide_related=true&amp;amp;show_comments=false&amp;amp;show_user=true&amp;amp;show_reposts=false&amp;amp;show_teaser=false"&gt;&lt;/iframe&gt;
&lt;/div&gt;

</summary>
    <content type="html">&lt;div class="song-metadata"&gt;
    &lt;span class="song-date"&gt;
      Written &amp;amp; Recorded: &lt;time&gt;Spring 2019&lt;/time&gt;
    &lt;/span&gt;
&lt;/div&gt;

&lt;p&gt;I’m pretty proud of this one. This was my first demo recording of it just to get it down, and I hadn’t yet written the bridge or the second half, but I think has the feeling I’m going for in it. Somber lyrics paired with a little skip in your step, ya know?&lt;/p&gt;

&lt;div class="soundcloud-player"&gt;

&lt;iframe width="100%" height="166" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/1198303366%3Fsecret_token%3Ds-B5YzeYS2rr3&amp;amp;auto_play=false&amp;amp;hide_related=true&amp;amp;show_comments=false&amp;amp;show_user=true&amp;amp;show_reposts=false&amp;amp;show_teaser=false"&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;This version is the full song, adding a bridge and another verse, but I think at this tempo with just a piano it sounds way too sad. Pretty, but sad.&lt;/p&gt;

&lt;div class="soundcloud-player"&gt;

&lt;iframe width="100%" height="166" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https://soundcloud.com/jeffkeen/everything-is-not-lost&amp;amp;auto_play=false&amp;amp;hide_related=true&amp;amp;show_comments=false&amp;amp;show_user=true&amp;amp;show_reposts=false&amp;amp;show_teaser=false"&gt;&lt;/iframe&gt;
&lt;/div&gt;

</content>
  </entry>
  <entry>
    <title>FCC Ruby Gem</title>
    <link rel="alternate" href="https://jeffkeen.com/p/fcc-gem/"/>
    <id>https://jeffkeen.com/p/fcc-gem/</id>
    <published>2019-06-01T00:00:00-05:00</published>
    <updated>2019-06-01T00:00:00-05:00</updated>
    <author>
      <name>Jeff Keen</name>
    </author>
    <summary type="html">&lt;p&gt;I just updated a gem I wrote in 2011 (which the FCC actually starred and forked, lol) to use their new API, which apparently knows about caching now. It doesn’t provide all the same data as the old one did, which is kinda weird. No “signal strength”? Why? So the gem can still query the old, horrifically slow and crusty API if you want it to.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;table class="rouge-table"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="rouge-gutter gl"&gt;&lt;pre class="lineno"&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
&lt;/pre&gt;&lt;/td&gt;&lt;td class="rouge-code"&gt;&lt;pre&gt;&lt;span class="n"&gt;station&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;FCC&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:fm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"KOOP"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists?&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;licensed?&lt;/span&gt;
  &lt;span class="c1"&gt;#Basic attributes, available quickly because the FCC actually caches these in a CDN: &lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 65320&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; LICENSED&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rf_channel&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 219&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;license_expiration_date&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "08/01/2021"&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;facility_type&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; ED&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frequency&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 91.7 &lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contact&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; &amp;lt;struct FCC::Station::Contact&amp;gt;&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; &amp;lt;struct FCC::Station::Contact&amp;gt;&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;community&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; &amp;lt;struct FCC::Station::Community city="HORNSBY", state="TX"&amp;gt;&lt;/span&gt;

  &lt;span class="c1"&gt;# Extended attributes, takes several seconds to load initially because the FCC is running this endpoint on a 1960s era mainframe operated by trained hamsters. &lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;station_class&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; A&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signal_strength&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 3.0 kW&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;antenna_type&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; ND&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;effective_radiated_power&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 3.0 kW&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;haat_horizontal&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 26.0&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;haat_vertical&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 26.0&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;latitude&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "30.266861111111112"&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;longitude&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "-97.67444444444445"&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;file_number&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; BLED-19950103KA&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</summary>
    <content type="html">&lt;p&gt;I just updated a gem I wrote in 2011 (which the FCC actually starred and forked, lol) to use their new API, which apparently knows about caching now. It doesn’t provide all the same data as the old one did, which is kinda weird. No “signal strength”? Why? So the gem can still query the old, horrifically slow and crusty API if you want it to.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;table class="rouge-table"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="rouge-gutter gl"&gt;&lt;pre class="lineno"&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
&lt;/pre&gt;&lt;/td&gt;&lt;td class="rouge-code"&gt;&lt;pre&gt;&lt;span class="n"&gt;station&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;FCC&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:fm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"KOOP"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists?&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;licensed?&lt;/span&gt;
  &lt;span class="c1"&gt;#Basic attributes, available quickly because the FCC actually caches these in a CDN: &lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 65320&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; LICENSED&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rf_channel&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 219&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;license_expiration_date&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "08/01/2021"&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;facility_type&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; ED&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frequency&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 91.7 &lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contact&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; &amp;lt;struct FCC::Station::Contact&amp;gt;&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; &amp;lt;struct FCC::Station::Contact&amp;gt;&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;community&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; &amp;lt;struct FCC::Station::Community city="HORNSBY", state="TX"&amp;gt;&lt;/span&gt;

  &lt;span class="c1"&gt;# Extended attributes, takes several seconds to load initially because the FCC is running this endpoint on a 1960s era mainframe operated by trained hamsters. &lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;station_class&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; A&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signal_strength&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 3.0 kW&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;antenna_type&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; ND&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;effective_radiated_power&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 3.0 kW&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;haat_horizontal&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 26.0&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;haat_vertical&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 26.0&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;latitude&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "30.266861111111112"&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;longitude&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; "-97.67444444444445"&lt;/span&gt;
  &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;file_number&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; BLED-19950103KA&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content>
  </entry>
  <entry>
    <title>Blame</title>
    <link rel="alternate" href="https://jeffkeen.com/p/blame/"/>
    <id>https://jeffkeen.com/p/blame/</id>
    <published>2019-02-01T05:00:00-06:00</published>
    <updated>2019-02-01T05:00:00-06:00</updated>
    <author>
      <name>Jeff Keen</name>
    </author>
    <summary type="html">&lt;div class="song-metadata"&gt;
    &lt;span class="song-date"&gt;
      Written &amp;amp; Recorded: &lt;time&gt;Early 2019&lt;/time&gt;
    &lt;/span&gt;
&lt;/div&gt;

&lt;div class="soundcloud-player"&gt;

&lt;iframe width="100%" height="166" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https://soundcloud.com/jeffkeen/blame&amp;amp;auto_play=false&amp;amp;hide_related=true&amp;amp;show_comments=false&amp;amp;show_user=true&amp;amp;show_reposts=false&amp;amp;show_teaser=false"&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;div class="comment-timebox"&gt;
  2021 feelings about it
&lt;/div&gt;

&lt;p&gt;(Nobody to blame but myself for the one line that’s embarassingly off pitch. Randy Jackson would have a field day on me.)&lt;/p&gt;
</summary>
    <content type="html">&lt;div class="song-metadata"&gt;
    &lt;span class="song-date"&gt;
      Written &amp;amp; Recorded: &lt;time&gt;Early 2019&lt;/time&gt;
    &lt;/span&gt;
&lt;/div&gt;

&lt;div class="soundcloud-player"&gt;

&lt;iframe width="100%" height="166" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https://soundcloud.com/jeffkeen/blame&amp;amp;auto_play=false&amp;amp;hide_related=true&amp;amp;show_comments=false&amp;amp;show_user=true&amp;amp;show_reposts=false&amp;amp;show_teaser=false"&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;div class="comment-timebox"&gt;
  2021 feelings about it
&lt;/div&gt;

&lt;p&gt;(Nobody to blame but myself for the one line that’s embarassingly off pitch. Randy Jackson would have a field day on me.)&lt;/p&gt;
</content>
  </entry>
</feed>
