<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jake McLelland</title>
    <description>The latest articles on DEV Community by Jake McLelland (@jakemclelland).</description>
    <link>https://dev.to/jakemclelland</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F811582%2F1df57e6d-1bad-48cf-a381-c9727c8b4bbc.jpg</url>
      <title>DEV Community: Jake McLelland</title>
      <link>https://dev.to/jakemclelland</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jakemclelland"/>
    <language>en</language>
    <item>
      <title>Sometimes the Best Work Ends with No Work at All</title>
      <dc:creator>Jake McLelland</dc:creator>
      <pubDate>Thu, 03 Jul 2025 21:03:27 +0000</pubDate>
      <link>https://dev.to/jakemclelland/sometimes-the-best-work-ends-with-no-work-at-all-2hig</link>
      <guid>https://dev.to/jakemclelland/sometimes-the-best-work-ends-with-no-work-at-all-2hig</guid>
      <description>&lt;p&gt;Most developers I know are eager to dive into code. Honestly, I am, too! There's something satisfying about building solutions, seeing immediate results, and having tangible commits to show for your time. But over the years, I've learned that some of the &lt;strong&gt;&lt;em&gt;most valuable work&lt;/em&gt;&lt;/strong&gt; happens outside of the IDE.&lt;/p&gt;

&lt;p&gt;Recently, I received a ticket to fix a "bug" where tax calculations were incorrect on invoices. The obvious move would’ve been to tweak the tax logic, maybe add some conditional statements and patch it quickly. Instead, I paused and spent a little time tracing the data flow. Stepping through the frontend display, the server-side API code, down to the mainframe data imports.&lt;/p&gt;

&lt;p&gt;That day, I discovered that the app wasn't broken at all. It was correctly calculating tax based on the data it received. The real issue was a single incorrect value in the client's mainframe system. The fix? A simple data update on their end. No code required.&lt;/p&gt;

&lt;p&gt;This kind of discovery work doesn't generate impressive pull requests or flashy demos. But it protected the client’s development budget for features that really mattered, and prevented us from building a workaround that would have masked the real problem.&lt;/p&gt;

&lt;p&gt;I've found that taking discovery seriously, even when it doesn't generate code, often produces more value. It's detective work that pays dividends in time saved, technical debt avoided, and clients who trust that you're solving their real problems, not just the symptoms they present.&lt;/p&gt;

&lt;p&gt;The best developers I know aren't just good at writing code. They're good at knowing when &lt;strong&gt;&lt;em&gt;not to write&lt;/em&gt;&lt;/strong&gt; it at all.&lt;/p&gt;

&lt;p&gt;What are your thoughts? Have you ever “developed” a solution by writing no code at all?&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Some thoughts on AI</title>
      <dc:creator>Jake McLelland</dc:creator>
      <pubDate>Mon, 14 Apr 2025 19:40:04 +0000</pubDate>
      <link>https://dev.to/jakemclelland/some-thoughts-on-ai-5g6i</link>
      <guid>https://dev.to/jakemclelland/some-thoughts-on-ai-5g6i</guid>
      <description>&lt;p&gt;As a professional software developer, I’ve noticed it’s becoming increasingly difficult to avoid using AI. CoPilot is now built into Microsoft products like Word, Excel, and PowerPoint, to name a few. Most IDEs, like Visual Studio and Xcode, now have AI enabled by default to assist with coding.&lt;/p&gt;

&lt;p&gt;There’s a difference between deterministic and non-deterministic AI. If you’ve ever googled anything or used spell check, you’ve already used a mix of deterministic (or pre-programmed) and non-deterministic or probabilistic AI. For the rest of this post, what I mean by “AI” is the non-deterministic generative AI, using tools like Perplexity, ChatGPT, and more specifically CoPilot.&lt;/p&gt;

&lt;p&gt;While I’m often amazed by how AI appears to read my mind when it suggests the next bit of code, it’s obvious that it also has some limitations. My experience in using AI in the development process reminds me a lot of guiding a toddler through an obstacle course — I’m constantly adjusting and cajoling it to get the results I need. I often ignore its suggestions — and sometimes even defy them outright. Prompt engineering seems as important an art to learn now as is other aspects of development.&lt;/p&gt;

&lt;p&gt;At its core, AI is just a parrot imitating the style and syntax of the code surrounding it. That could be a good thing or a bad thing. For example, imagine walking into the subway in New York City. There’s trash everywhere and &lt;a href="https://www.governor.ny.gov/news/governor-hochul-announces-subway-fare-evasion-down-26-percent-new-york-city" rel="noopener noreferrer"&gt;1 out of every 10&lt;/a&gt; people who come through the system evade the fare by jumping the gates.&lt;/p&gt;

&lt;p&gt;If you were AI, you might conclude that these details define correct behavior in the context of the subway because that’s what the majority do. &lt;/p&gt;

&lt;p&gt;Just because something is common, popular, or even ubiquitous does NOT make it right!&lt;/p&gt;

&lt;p&gt;This is what “vibe coding” with AI gives you. &lt;/p&gt;

&lt;p&gt;The large language models that power AI are only predicting which word comes next. Sure, today’s more advanced models included lots of training and self-analysis. But, at the end of the day, AI is merely imitating what everybody else has already done, or what it already sees in your codebase.&lt;/p&gt;

&lt;p&gt;Or to use an early computing term: GIGO (garbage in, garbage out)! &lt;/p&gt;

&lt;p&gt;There’s a lot of brilliant software out there. There’s also a lot of trash. Even some of the training bootcamps I’ve seen don’t actually follow best practices and end up with some less than tidy code. Perhaps because a training project, or a portfolio capstone project has a pretty high &lt;a href="https://tonyalicea.dev/blog/entropy-tolerance-ai/" rel="noopener noreferrer"&gt;entropy tolerance&lt;/a&gt;, who cares if the code is tidy or not? But a real, professional production product deserves some careful thought. I’ve &lt;a href="https://dev.to/jakemclelland/syntactic-elegance-the-developers-dream-329b"&gt;always said&lt;/a&gt; that even though nobody except one or two other developers will ever see your code doesn’t mean the code can be sloppy! Production code especially should be as clean on the inside as it is intuitive on the outside.&lt;/p&gt;

&lt;p&gt;I hope that someone figures out a way to cut through all the politics of curating the “best practices” or “correct information”, and ignore all the “trash” of lazy (I mean brilliant) developers. At the end of the day, “vibe coding” with AI will only give you code that perhaps, possibly, maybe works??? (And yes my AI companion yelled curses at me to “fix” that last sentence!)&lt;/p&gt;

&lt;p&gt;Don’t get me wrong, I use AI all the time. But I believe AI works best as an analysis tool, the same way spell-check is a lifesaver for a document. It can do wonders for checking your work, debugging issues, and helping you simplify and optimize things. Relying on it to write ALL your code would be risky.&lt;/p&gt;

&lt;p&gt;AI has come a long way in the past few years, but I’m not holding my breath for Mr. Data to show up in the office any time soon! Perhaps we have reached the point of having a real &lt;a href="https://hitchhikers.fandom.com/wiki/Eddie_the_Computer" rel="noopener noreferrer"&gt;Eddie&lt;/a&gt; from HHGTTG, or maybe it’s more like &lt;a href="https://en.wikipedia.org/wiki/Holly_(Red_Dwarf)#Character" rel="noopener noreferrer"&gt;Holly&lt;/a&gt; from Red Dwarf. As long as we avoid HAL 9000 and Skynet, we’re good!&lt;/p&gt;

&lt;p&gt;What do you think? How do you use AI in your workflow?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>discuss</category>
      <category>vibecoding</category>
    </item>
    <item>
      <title>Syntactic Elegance-The Developer’s Dream</title>
      <dc:creator>Jake McLelland</dc:creator>
      <pubDate>Mon, 18 Sep 2023 23:49:45 +0000</pubDate>
      <link>https://dev.to/jakemclelland/syntactic-elegance-the-developers-dream-329b</link>
      <guid>https://dev.to/jakemclelland/syntactic-elegance-the-developers-dream-329b</guid>
      <description>&lt;p&gt;I &lt;a href="https://www.codepoetllc.com/blog/the-developers-dilemma-balancing-the-speed-quality-and-performance" rel="noopener noreferrer"&gt;recently wrote&lt;/a&gt; that developers have to strike a perfect balance of delivering high-quality software that works well, looks good, and is delivered on time. &lt;/p&gt;

&lt;p&gt;Of course, it's impossible to "have it all" unless all of your clients are fabulously wealthy and have all the time in the world.&lt;/p&gt;

&lt;p&gt;I wanted to delve more into the aspect of Syntactic Elegance. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Just because nobody will ever see the code, doesn't mean it can be sloppy!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Code That Speaks For Itself
&lt;/h2&gt;

&lt;p&gt;What I mean by Syntactic Elegance is code that appears to be simple and easy to follow, but that actually does a lot. Think of a high-end luxury sports car. With smooth curves and perfectly glossed paint, it seems both simple and supple at the same time. It got that way because someone spent a lot of time carefully refining and polishing it.&lt;/p&gt;

&lt;p&gt;Unfortunately, in most cases, clients can't afford to wait for you to test and refine every aspect of your code. But just a few habits go a long way to producing elegant code.&lt;/p&gt;

&lt;p&gt;Here are my top three recommendations&lt;/p&gt;

&lt;h3&gt;
  
  
  Avoid acronyms! Especially obvious ones.
&lt;/h3&gt;

&lt;p&gt;Consider a scenario where you have a model used solely for serializing a class sent back and forth between the server and client. Some might call that a &lt;a href="https://en.wikipedia.org/wiki/Plain_old_CLR_object" rel="noopener noreferrer"&gt;POCO&lt;/a&gt; object, or a &lt;a href="https://martinfowler.com/eaaCatalog/dataTransferObject.html" rel="noopener noreferrer"&gt;DTO&lt;/a&gt; model. Don't! Not because there's debate about the meaning of both DTO and POCO (or as my coworker yells each time "you don't need to say 'object' after 'DTO' or 'POCO' because the word 'object' is part of the acronym!'), but because it should be clearly obvious what something is simply by looking at it. All modern IDEs show you what an element is simply by hovering the mouse over it or navigating to the definition. Embedding the type of something in the name is tantamount to calling your manager "JimBoss"! Name an object for what it truly is! Instead of &lt;code&gt;CustomerDto&lt;/code&gt;, simply call it &lt;code&gt;Customer&lt;/code&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Spell things out!
&lt;/h3&gt;

&lt;p&gt;I once had a coworker who consistently used unnecessarily short and arbitrary abbreviations in their code. I had to hold myself together one day when they called me over to help work out some logic and I noticed they had used &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, and &lt;code&gt;z&lt;/code&gt; as variable names in code they had actually committed to the repository! To me, it seemed 2 parts lazy and 3 parts naive. Don't get me wrong, I tend to begin by inventing new code elements with whimsical variable names like &lt;code&gt;pizza&lt;/code&gt; and &lt;code&gt;cat&lt;/code&gt; until I understand their purpose and functionality. But as soon as you get to a point that you reasonably know what something is, give it a name!!! Humans have such a deep desire to name things that we arbitrarily name the full moon at perigee differently than the full moon in January differently than a second full moon during the same calendar month!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A good name conveys what the code does, what the object represents, and what the consequences of using it might be. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On one project, one developer wrote a method named &lt;code&gt;GetNextValue&lt;/code&gt; that returned the next logical value in an alpha-numeric sequence for a kind of record identifier. Seems innocent enough. But, under the hood, in order to get that "next" value, it inserted a row into a table that stored the value it came up with (to ensure it was unique,) and also updated the &lt;code&gt;Consumer&lt;/code&gt; table with that unique value. In other words, the method was anything but a simple &lt;code&gt;Get&lt;/code&gt;! It altered the database under the hood! A good name should reflect that, perhaps with a name like &lt;code&gt;RegisterConsumerRecord&lt;/code&gt; in this case. This is particularly crucial if other developers intend to reuse the method or if it might have unintended consequences (as was the case here). The day another developer innocently re-used that method had disastrous consequences because the poor naming of the methods they developed led both developers to misunderstood what the other's code did.&lt;/p&gt;

&lt;h3&gt;
  
  
  Observe the accent and style preferences of the language of the code
&lt;/h3&gt;

&lt;p&gt;Which is more correct: to place the opening curly brace in-line with a method definition, OR immediately below it on the next line?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myValue&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;DoStuff&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Figure A&lt;/p&gt;

&lt;p&gt;OR&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myValue&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;DoStuff&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Figure B&lt;/p&gt;

&lt;p&gt;The correct answer is: It depends on the programming language in which the code is written! Javascript developers prefer the style in Figure B, whereas C# developers prefer the style in Figure A. Both developers, who are both equally amazing, will say the other clearly just looks wrong! Should you use &lt;a href="https://en.wikipedia.org/wiki/Camel_case" rel="noopener noreferrer"&gt;camelCase&lt;/a&gt; or &lt;a href="https://en.wikipedia.org/wiki/Letter_case#Kebab_case" rel="noopener noreferrer"&gt;kebab-case&lt;/a&gt; for variable names or other elements? The correct answer is: It depends on the language. Every language comes with it's own "accent", and any time you need to switch contexts to another language, do everyone a favor and adapt your code style to the preferences of the language your code is in!&lt;/p&gt;

&lt;p&gt;You can't have it all, but cultivating a few simple habits can make your code more approachable for the next developer who follows in your footsteps!&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>development</category>
      <category>csharp</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Handling Duplicate Records</title>
      <dc:creator>Jake McLelland</dc:creator>
      <pubDate>Tue, 13 Dec 2022 21:47:25 +0000</pubDate>
      <link>https://dev.to/jakemclelland/handling-duplicate-records-4l57</link>
      <guid>https://dev.to/jakemclelland/handling-duplicate-records-4l57</guid>
      <description>&lt;p&gt;Let's talk about a scenario that comes up occasionally: &lt;br&gt;
How can I deal with duplicate records in a database?&lt;/p&gt;

&lt;p&gt;This happens for various reasons outside the scope of this article. The question is how to deal with them.&lt;/p&gt;

&lt;p&gt;You have a few options.&lt;/p&gt;

&lt;p&gt;Ideally, you would have some indexes on your table that enforces unique constraints. That way, duplicate data simply gets rejected by the database instead of sneaking in. In my case, the table in question is a generic log that includes lots of optional fields. The complexity prevented me from making any kind of unique constraints that made any sense.&lt;/p&gt;

&lt;p&gt;Another option is (if you have control over the software that inserts the records), you can update that software to first look for existing records instead of blindly doing inserts. If you can't apply unique constraints to the table at the database level, this is your next best option, and is what I did. However, I still had all the duplicates sitting there in the database from before I added the code to my API to protect against them.&lt;/p&gt;

&lt;p&gt;If you have just a few dupes, you could manually locate them, and manually execute delete statements using whatever primary keys or other specific data that identifies just the duplicate records. The drawback here is that you could end up accidentally deleting non-dupes, other data, or worse, the whole table. Terrifying, isn't it! (This is what keeps DBA's up at night! "Don't forget the WHERE clause!!" you'll hear them scream as they wake up from their nightmare, sweating and shaking uncontrollably.)&lt;/p&gt;

&lt;p&gt;There are probably other options. Here's a solution that worked for me.&lt;/p&gt;

&lt;p&gt;I'm using a Microsoft SQL Server database hosted on an Azure server. So, I can take advantage of some slightly non-ISO standard SQL syntax. (you could easily modify this slightly to work with whatever DB platform you’re running)&lt;/p&gt;

&lt;p&gt;My solution uses a cursor. &lt;/p&gt;

&lt;p&gt;Now, I know. I could hear you gasp just then and click your tongue at me. But rest assured, this is probably one of only, . . . well honestly, I can’t remember any other time I've ever been forced to use a cursor when I could do anything otherwise. (Full disclosure, I have actually approved the code review of someone else who was forced to use a cursor, and I've also been forced the indignity of maintaining someone else's code base that included some cursors for whatever reason. Don't judge me!)&lt;/p&gt;

&lt;p&gt;The basic overview is that we need to declare some variables to hold some values we can use for finding and matching the duplicates. Then we set up the cursor, preferably with FORWARD_ONLY specified so we keep things lite on the server. Next, we begin a loop that first finds one "good" record per dupe-set, and then deletes all the other matching records except that good one. Finally, don't forget to close and deallocate the cursor so you can pretend you never used one and feign ignorance.&lt;/p&gt;

&lt;p&gt;Now for the good stuff.&lt;/p&gt;

&lt;p&gt;(Variable, table, and column names obfuscated below to protect the innocent)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Set up the variables&lt;/span&gt;
&lt;span class="k"&gt;DECLARE&lt;/span&gt; 
  &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;countOfDupes&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;transactionDate&lt;/span&gt; &lt;span class="n"&gt;datetime2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
  &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;customerId&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;invoiceId&lt;/span&gt; &lt;span class="n"&gt;nvarchar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
  &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;transactionType&lt;/span&gt; &lt;span class="n"&gt;nvarchar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;-- Prepare the cursor we'll use&lt;/span&gt;
&lt;span class="k"&gt;DECLARE&lt;/span&gt; &lt;span class="n"&gt;logDupes&lt;/span&gt; &lt;span class="k"&gt;CURSOR&lt;/span&gt; &lt;span class="n"&gt;FORWARD_ONLY&lt;/span&gt; 
&lt;span class="k"&gt;FOR&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;CountOfDupes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;TransactionDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;CustomerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;InvoiceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;TransactionType&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;TransactionLogs&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;TransactionDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CustomerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;InvoiceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TransactionType&lt;/span&gt;
&lt;span class="k"&gt;HAVING&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;)&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="c1"&gt;-- only the ones that have duplicates&lt;/span&gt;

&lt;span class="k"&gt;OPEN&lt;/span&gt; &lt;span class="n"&gt;logDupes&lt;/span&gt;

&lt;span class="k"&gt;FETCH&lt;/span&gt; &lt;span class="k"&gt;NEXT&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;logDupes&lt;/span&gt;   
&lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;countOfDupes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;transactionDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;invoiceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;transactionType&lt;/span&gt;

&lt;span class="c1"&gt;-- Now loop through each set that has duplicates&lt;/span&gt;
&lt;span class="n"&gt;WHILE&lt;/span&gt; &lt;span class="o"&gt;@@&lt;/span&gt;&lt;span class="n"&gt;FETCH_STATUS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
    &lt;span class="n"&gt;PRINT&lt;/span&gt; &lt;span class="n"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Handling '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;countOfDupes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;' duplicates for customer: '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;' on &lt;/span&gt;&lt;span class="se"&gt;''&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;transactionDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;''&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;-- First, get the @firstMatchingId for just the top 1 matching result&lt;/span&gt;
    &lt;span class="k"&gt;DECLARE&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;firstMatchingId&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;

    &lt;span class="k"&gt;SELECT&lt;/span&gt; 
      &lt;span class="n"&gt;TOP&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; 
      &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;firstMatchingId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;TransactionLogs&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;CustomerId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;customerId&lt;/span&gt;
    &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;TransactionDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;transactionDate&lt;/span&gt;
    &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;InvoiceId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;invoiceId&lt;/span&gt;
    &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;TransactionType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;transactionType&lt;/span&gt;

    &lt;span class="c1"&gt;-- Now use that @firstMatchingId to delete all the matching results EXCEPT FOR the top 1&lt;/span&gt;
    &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt;
    &lt;span class="n"&gt;TransactionLogs&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;firstMatchingId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;CustomerId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;customerId&lt;/span&gt;
    &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;TransactionDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;transactionDate&lt;/span&gt;
    &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;InvoiceId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;invoiceId&lt;/span&gt;
    &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;TransactionType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;transactionType&lt;/span&gt;

    &lt;span class="c1"&gt;-- Continue the loop&lt;/span&gt;
    &lt;span class="k"&gt;FETCH&lt;/span&gt; &lt;span class="k"&gt;NEXT&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;logDupes&lt;/span&gt;   
    &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;countOfDupes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;transactionDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;invoiceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;transactionType&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;

&lt;span class="c1"&gt;-- Dont forget to close and deallocate the cursor!&lt;/span&gt;
&lt;span class="k"&gt;CLOSE&lt;/span&gt; &lt;span class="n"&gt;logDupes&lt;/span&gt;  
&lt;span class="k"&gt;DEALLOCATE&lt;/span&gt; &lt;span class="n"&gt;logDupes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See  &lt;a href="https://learn.microsoft.com/en-us/sql/t-sql/language-elements/declare-cursor-transact-sql?view=sql-server-ver16" rel="noopener noreferrer"&gt;Microsoft's documentation&lt;/a&gt; for specific guidance on using cursors in SQL Server.&lt;/p&gt;

&lt;p&gt;If you've found a better way to delete a bunch of duplicate records from your db, I'd love to hear your story!&lt;/p&gt;

&lt;p&gt;Hope you find this amusing, or possibly helpful. &lt;/p&gt;

</description>
      <category>hiring</category>
      <category>interview</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Combining two datasets into one</title>
      <dc:creator>Jake McLelland</dc:creator>
      <pubDate>Sat, 20 Aug 2022 18:42:38 +0000</pubDate>
      <link>https://dev.to/jakemclelland/combining-two-datasets-into-one-3826</link>
      <guid>https://dev.to/jakemclelland/combining-two-datasets-into-one-3826</guid>
      <description>&lt;p&gt;The other day I had a client request that we add square pegs to a round hole. We needed to return two, very different objects into a single collection. This happens all the time in this industry, of course. And it's obviously not a big deal to add a new object that can represent the good bits from both objects as one conjoined unit.&lt;/p&gt;

&lt;p&gt;But, one of the more interesting challenges that resulted was that I needed a search method that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Definitely returns objects from both distinct data sets&lt;/li&gt;
&lt;li&gt;Still honors an overall &lt;code&gt;take&lt;/code&gt; limiter from the client application. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In other words, if the client app request only 10 records, but there are some of both data sets, how do you limit the final results so that there are definitely some of both (if possible) and yet the overall return stops at the requested limit?&lt;/p&gt;

&lt;p&gt;If there are no results in the first dataset, but some in the other, then clearly you can just use the requested take count. &lt;/p&gt;

&lt;p&gt;It gets tricky, though, when there are a few in both, especially when there are a bunch in one but only one or two in the other.&lt;/p&gt;

&lt;p&gt;Let's say the client app requested 10 records, one dataset returns  9, and the other 1. As a human, I read that and it's a no brainer. We can take all of both, it's less then 10, all good. But what if there are 6 of one and 42 of the other? Or thousands in both?&lt;/p&gt;

&lt;p&gt;I'm sure there's probably a more sophisticated way to handle this, either mathmatically, using AI, or something else more clever. But this is what I came up with. &lt;/p&gt;

&lt;p&gt;👇 &lt;a href="https://dotnetfiddle.net/fRhVId" rel="noopener noreferrer"&gt;Run this in dotnetfiddle&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;leftTakeCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;rightTakeCount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;GetTakeCountsFromTwoSourcesToMeetOneTotalQuota&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;take&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;leftCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;rightCount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;halfTake&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;take&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// since it's an int here, the decimal will just truncate&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;totalCount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;leftCount&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;rightCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;totalCountAboveTake&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;totalCount&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;take&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;leftAboveHalfTake&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;leftCount&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;halfTake&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;shouldReduceLeft&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;totalCountAboveTake&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;leftAboveHalfTake&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;rightAboveHalfTake&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rightCount&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;halfTake&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;shouldReduceRight&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;totalCountAboveTake&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;rightAboveHalfTake&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// When both counts are above the requested take count and also above the halfTake, then we can just chop both down to the half count&lt;/span&gt;
    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;canReduceBothToHalftake&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;totalCountAboveTake&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;leftAboveHalfTake&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;rightAboveHalfTake&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;leftReductionToHalfTake&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;canReduceBothToHalftake&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;leftCount&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;halfTake&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;rightReductionToHalfTake&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;canReduceBothToHalftake&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;rightCount&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;halfTake&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// When one of the counts is below halfTake, we need to calculate the difference so that we'll end up with some of both&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;leftReductionToDifference&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shouldReduceLeft&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;canReduceBothToHalftake&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;leftCount&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;take&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;rightCount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;rightReductionToDifference&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shouldReduceRight&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;canReduceBothToHalftake&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;rightCount&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;take&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;leftCount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Since we used booleans above, one of these will end up as 0. So get the other one&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;maxLeftReduction&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;leftReductionToHalfTake&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;leftReductionToDifference&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;maxRightReduction&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rightReductionToHalfTake&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rightReductionToDifference&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;finalLeftReduction&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;shouldReduceLeft&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;maxLeftReduction&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;finalRightReduction&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;shouldReduceRight&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;maxRightReduction&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;leftTakeCount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;leftCount&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;finalLeftReduction&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;rightTakeCount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rightCount&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;finalRightReduction&lt;/span&gt;&lt;span class="p"&gt;;&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;leftTakeCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rightTakeCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, I assume that if the sum of both counts is less than or equal to the requested &lt;code&gt;take&lt;/code&gt; amount, then we can just use all of both. But if the sum is more than that, we need to intelligently reduce the larger side to leave room for the smaller side. And if there's just a bunch in both sets, we can just cut the requested &lt;code&gt;take&lt;/code&gt; value in half and return that many of both values.&lt;/p&gt;

&lt;p&gt;The result is a tuple that gives me the &lt;code&gt;take&lt;/code&gt; value of the first or 'left' dataset separately from the second or 'right' dataset. That allows me to limit my database query for both sets so that the total number of records is whatever the client requested.&lt;/p&gt;

&lt;p&gt;One obvious issue that I don't have time to address here is that since I'm using an &lt;code&gt;int&lt;/code&gt; as the &lt;code&gt;halfTake&lt;/code&gt; value and therefore disposing of the remainder decimal values, if you request an uneven number like 25, then the &lt;code&gt;halfTake&lt;/code&gt; will end up one rounding up to the nearest &lt;code&gt;int&lt;/code&gt;, and you'll end up with one extra record.&lt;/p&gt;

&lt;p&gt;But, since I control the client app, . . . I'm not going to do that, and just not worry about that for right now. Besides, my consuming service method actually has to sort these values later, so I can just lop off the extras with another &lt;code&gt;.Take(request.Take)&lt;/code&gt; statement in the service before returning it to the client.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dataset</category>
      <category>devjournal</category>
    </item>
  </channel>
</rss>
