Sunday, June 29, 2025

changeDetectorRef.markForCheck()

When using changeDetection: ChangeDetectionStrategy.OnPush, you need to call changeDetectorRef.markForCheck(). To avoid explicitly calling markForCheck, use async pipe on the observable variable. See line 13 on HTML below
@Component({
  selector: 'app-contact-us',
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [JsonPipe, CommonModule, FormsModule, JsonPipe],
  templateUrl: './contact-us.html',
  styles: ``,
})

export class ContactUs implements OnInit {
  weather: any = null;
  weatherToo: any = null;
  weather$: any = null;
  weatherToo$: any = null;
  
  // not working
  // @Inject(ChangeDetectorRef) private readonly cdr!: ChangeDetectorRef;

  // working, but the obvious benefits are still not clear
  // private readonly cdr = inject(ChangeDetectorRef);

  constructor(private http: HttpClient, private cdr: ChangeDetectorRef) {}

  ngOnInit(): void  {
    this.http.get('/api/WeatherForecast').subscribe({
      next: value => {
        // console.log({value});
        this.weather = value;
        this.cdr.markForCheck(); // will reflect on the UI

        // use this when you need it be immediate, like in setTimeout or 3rd party library
        // this.cdr.detectChanges();
      },
      error: (err) => console.error('http error', err)
    });

    firstValueFrom(this.http.get('/api/WeatherForecast')).then(value => {
      this.weatherToo = value;
      this.cdr.markForCheck();
    });

    this.weather$ = this.http.get('/api/WeatherForecast');
    // this.weather$.subscribe({
    //   next: (value: any) => {
    //     console.log('see', {value});
    //   },
    //   error: (err: any) => console.error('http error', err)
    // });

    this.weatherToo$ = this.http.get('/api/WeatherForecast');
  }
}
<div>
  <hr/>
  This works:<br/>
  {{weather | json}}
  <hr/>
  This too:<br/>
  {{weatherToo | json}}
  <hr/>
  This too, and it implicitly calls changeDetectorRef.markForCheck():<br/>
  {{weather$ | async | json}}

  <hr/>
  This will not show null:<br/>
  <ng-container *ngIf="weatherToo$ | async as weatherWhenHaveValue">{{ weatherWhenHaveValue | json }}</ng-container>

  <hr/>
  <input [(ngModel)]="name" placeholder="Name"/>
</div>
Ask this on Gemini or ChatGPT and see the answer:
does this recommendation kinda defeat the purpose of optimization for when change detection should take effect?

Option 3: Use AsyncPipe in the template
This forces Angular to update on observable emissions, even in OnPush:

weather$ = this.http.get('/api/WeatherForecast');

{{ weather$ | json }}

Saturday, June 28, 2025

Angular: proxy.conf.json; React: vite proxy

Angular

angular.json
        "serve": {
          "options": {
            "ssl": true,
            "sslKey": "ssl/key.pem",
            "sslCert": "ssl/cert.pem",
            "proxyConfig": "proxy.conf.json"
          },
          "builder": "@angular/build:dev-server",
          "configurations": {
            "production": {
              "buildTarget": "react-analogous:build:production"
            },
            "development": {
              "buildTarget": "react-analogous:build:development"
            }
          },
          "defaultConfiguration": "development"
        },
proxy.conf.json
secure = false, just a self-signed certificate
{
  "/api": {
    "target": "https://localhost:7249",
    "secure": false,
    "changeOrigin": true,
    "logLevel": "debug"
  }
}

React

React's vite proxy
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'

import fs from 'fs';

// https://vite.dev/config/
export default defineConfig({
  plugins: [react()],
  server: {
    https: {
      key: fs.readFileSync('localhost-key.pem') ,
      cert: fs.readFileSync('localhost.pem'),
    },
    port: 3000,
    proxy: {
      '/api': { // This is the prefix for requests you want to proxy
        target: 'https://localhost:7249', // The URL of your backend API
        changeOrigin: true, // Rewrites the origin header to match the target URL
        // rewrite: (path) => path.replace(/^\/api/, ''), // Optional: remove '/api' from the request path sent to the backend
        secure: false, // Optional: if your backend uses HTTPS but has a self-signed certificate
        // agent: new http.Agent(), // Optional: if using HTTPS with a specific agent
      },
    },
  },
})

Friday, June 27, 2025

Angular: Type 'Event' is not assignable to type 'string'.

If this innocent-looking code
Color: <input [(ngModel)]="color" />
gives you this error:
Type 'Event' is not assignable to type 'string'.
Add FormsModule on imports:
@Component({
  selector: 'app-root',
  standalone: true,
  imports: [
    HoverHighlightDirective,
    // FormsModule, // uncomment this to remove the error: Type 'Event' is not assignable to type 'string'.
  ],
  templateUrl: './main.html',
})
export class App {
  name = 'Angular';

  color: string = 'blue';
}

bootstrapApplication(App);
Test code on stackblitz: https://stackblitz.com/edit/angular-jjw6g1cv?file=src%2Fmain.ts

React: Just JavaScript ternary; Angular: ng-container, ngIf, else, ng-template

React

import { useState } from 'react';
import './App.css';

function App() {
  const [theme, setTheme] = useState('light');

  return (
    <>
      <h1>React conditional rendering</h1>

      {theme === 'light' ? (
        <>
          <div>Hello Lightness</div>
          <p></p>
        </>
      ) : (
        <>
          <div>Hello Darkness</div>
          <p></p>
        </>
      )}

      <hr />
      <button onClick={toggleTheme}>Toggle Theme (current: {theme})</button>
    </>
  );

  function toggleTheme() {
    setTheme((prev) => (prev === 'light' ? 'dark' : 'light'));
  }
}

export default App;

Angular

<h1>Angular Conditional Rendering</h1>

<ng-container *ngIf="theme === 'light'; else darkness">
  <div>Hello Lightness</div>
  <p></p>
</ng-container>

<ng-template #darkness>
  <div>Hello Darkness</div>
  <p></p>
</ng-template>

<br />
<button (click)="toggleTheme()">Toggle Theme (Current: {{theme}})</button>
import 'zone.js';
import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './main.html',
})
export class App {
  name = 'Angular';

  theme = 'light';

  toggleTheme() {
    this.theme = this.theme === 'light' ? 'dark' : 'light';
  }
}

bootstrapApplication(App);
Angular conditional renderingReact conditional rendering

Thursday, June 26, 2025

React: Context; Angular: @Injectable

Bootstrapping React

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + React + TS</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import './index.css';
import { ThemeProvider } from './contexts/ThemeContext.tsx';

// Just for demo that context can reach even on children, grandchildren etc
function Main() {
  return <App></App>;
}

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
  <React.StrictMode>
    <ThemeProvider>
      <Main />
    </ThemeProvider>
  </React.StrictMode>
);

Bootstrapping Angular

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>My app</title>
    <meta charset="UTF-8" />
  </head>
  <body>
    <main-root>Loading...</main-root>
  </body>
</html>
import 'zone.js';
import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { AsyncPipe, NgStyle } from '@angular/common';

import { App } from './app/app';

@Component({
  selector: 'main-root',
  standalone: true,
  imports: [AsyncPipe, NgStyle, App],
  template: `<app-root></app-root>`,
})
// Just for demo that service can reach even on children, grandchildren etc
export class Main {
  name = 'Angular';
}

bootstrapApplication(Main);

React's App component

import './App.css';
import { useTheme } from './contexts/ThemeContext';

export default function App() {
  // Destructuring make names lose context
  // const { theme, toggleTheme } = useTheme();

  // I feel explicit is better, so things won't appear as local
  const themeContext = useTheme();

  const name = 'App';

  return (
    <div
      style={{
        background: themeContext.theme === 'dark' ? '#222' : '#eee',
        color: themeContext.theme === 'dark' ? '#eee' : '#222',
        padding: '2rem',
      }}
    >
      <h1>Hello from {name}!</h1>
      <h3 onClick={themeContext.toggleTheme}>
        {themeContext.theme.toUpperCase()} MODE
      </h3>
      <a target="_blank" href="https://react.dev/">
        Learn more about React
      </a>
    </div>
  );
}

Angular's app-root component

import { Component } from '@angular/core';
import { ThemeContext } from './contexts/theme.context';
import { AsyncPipe, NgStyle } from '@angular/common';

@Component({
  selector: 'app-root',
  standalone: true,
  templateUrl: './app.html',
  imports: [NgStyle, AsyncPipe],
})
export class App {
  name = 'App';

  // This works too, just seems complicated, and also it looks like local properties
  // theme$: Observable<Theme>;
  // toggleTheme: () => void;

  // This works too, but it looks like local properties
  // get theme$() { return this.themeContext.theme$; }
  // toggleTheme() { this.themeContext.toggleTheme(); }

  constructor(public themeContext: ThemeContext) {
    // This works too, just seems complicated, and also it looks like local properties
    // ({theme$: this.theme$, toggleTheme: this.toggleTheme } = themeContext);
    // this.toggleTheme = this.toggleTheme.bind(themeContext);
    //
    // This works too, just seems complicated, and also it looks like local properties
    // this.theme$ = this.themeContext.theme$;
    // this.toggleTheme = this.themeContext.toggleTheme.bind(themeContext);
  }
}
<div
  [ngStyle]="{
  background: (themeContext.theme$ | async) === 'dark' ? '#222' : '#eee',
  color: (themeContext.theme$ | async) === 'dark' ? '#eee' : '#222',
  padding: '2rem'
}"
>
  <h1>Hello from {{ name }}!</h1>
  <h3 (click)="themeContext.toggleTheme()">
    {{(themeContext.theme$ | async)?.toUpperCase()}} MODE
  </h3>
  <a target="_blank" href="https://angular.dev/overview">
    Learn more about Angular
  </a>
</div>

React Context

import { createContext, ReactNode, useContext, useState } from 'react';

type Theme = 'light' | 'dark';

interface ThemeContextType {
  theme: Theme;
  toggleTheme: () => void;
}

const ThemeContext = createContext<ThemeContextType | undefined>(undefined);

export function ThemeProvider({ children }: { children: ReactNode }) {
  const [theme, setTheme] = useState<Theme>('light');

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );

  function toggleTheme() {
    setTheme((prev) => (prev === 'light' ? 'dark' : 'light'));
  }
}

export function useTheme(): ThemeContextType {
  const context = useContext(ThemeContext);

  if (!context) {
    throw new Error('useTheme must be used within a ThemeProvider');
  }

  return context;
}

Angular @Injectable

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

export type Theme = 'light' | 'dark';

@Injectable({
  providedIn: 'root',
})
export class ThemeContext {
  private themeSubject = new BehaviorSubject<Theme>('light');
  theme$ = this.themeSubject.asObservable();

  toggleTheme() {
    const newTheme = this.themeSubject.value === 'light' ? 'dark' : 'light';
    this.themeSubject.next(newTheme);
  }
}
React ContextAngular @Injectable

Wednesday, June 25, 2025

Angular: @Input @Output; React: Just JavaScript

Angular @Input @Output



Parent HTML:
<div style="border: 1px solid green; padding: 10px;">
  <h2>Home Component</h2>
  <p>Routing message: {{ (routeData$ | async)?.message }}</p>
  <button (click)="increase()">Increase</button>
  <app-contact-us [phoneNumber]="myPhone.toString()" mainOffice="something" [initialEmployeeCount]="homeEmployeeCount"
    (onEmployeeCountChanged)="handleEmployeeCountChanged($event)"></app-contact-us>
  <hr />
  Parent employee count: {{homeEmployeeCount}}
  <br/>
</div>
Parent TypeScript:
import { AsyncPipe, JsonPipe } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { map, Observable, tap } from 'rxjs';
import { ContactUs } from './contact-us/contact-us';
import { HomeResolverData } from '../../resolvers/home.resolver';

@Component({
  selector: 'app-home',
  imports: [AsyncPipe, ContactUs],
  templateUrl: './home.html',
  styles: ``,
})
export class Home implements OnInit {
  /**
   *
   */

  myPhone = 1314;
  homeEmployeeCount = 7;

  routeData$!: Observable<HomeResolverData>;

  constructor(private route: ActivatedRoute) {
    // console.log(JSON.stringify(this.route.data));
    // console.log(this.routeData$);
  }
  ngOnInit(): void {
    this.routeData$ = this.route.data.pipe(
      map((resolved) => resolved['homeResolver'] as HomeResolverData),
      tap((data) =>
        console.log(
          'RESOLVER_DATA_CONSUMED: Resolver data is now available in component:',
          data
        )
      )
    );
  }

  increase() {
    ++this.myPhone;
  }

  handleEmployeeCountChanged($event: number) {
    this.homeEmployeeCount = $event;
  }
}
Child HTML:
<div>

  <h4>Contact Us</h4>


  Phone: {{phoneNumber}}<br/>
  Main Office: {{primaryOffice}}<br/>

  <button (click)="welcomeEmployee()">Welcome employee</button>
</div>
Child TypeScript:
import { Component, EventEmitter, Input, Output } from '@angular/core';

@Component({
  selector: 'app-contact-us',
  imports: [],
  templateUrl: './contact-us.html',
  styles: ``
})
export class ContactUs {
    @Input() phoneNumber = '';
    @Input('mainOffice') primaryOffice!: string;

    @Input() initialEmployeeCount = 0;
    @Output() onEmployeeCountChanged = new EventEmitter<number>();

    welcomeEmployee() {
      ++this.initialEmployeeCount;
      this.onEmployeeCountChanged.emit(this.initialEmployeeCount);
    }


}


React



Parent HTML+TypeScript:
import { useLoaderData } from "react-router";
import { ContactUs } from "./Home/ContactUs";
import { useState } from "react";

export default function Home() {
    const homeData = useLoaderData();

    const [myPhone, setMyPhone] = useState(1314);
    const [homeEmployeeCount, setHomeEmployeeCount] = useState(7);

    return (
        <>
            <h2>Home Component</h2>
            <p>Routing message: {homeData.message}</p>
            <button onClick={increase}>Increase</button>
            <ContactUs
                phoneNumber={myPhone.toString()}
                mainOffice="something"
                initialEmployeeCount={homeEmployeeCount}
                onEmployeeCountChanged={handleEmployeeCountChanged}
            />
            <hr />
            <button onClick={batchIncreaseFlawed}>Batch Increase Flawed</button>
            <button onClick={batchIncreaseCorrect}>
                Batch Increase Correct
            </button>
            <hr />
            Parent employee count: {homeEmployeeCount}
        </>
    );

    function handleEmployeeCountChanged(newCount: number) {
        setHomeEmployeeCount(newCount);
    }

    function batchIncreaseFlawed() {
        setMyPhone(myPhone + 1);
        setMyPhone(myPhone + 1);
        setMyPhone(myPhone + 1);
    }

    function batchIncreaseCorrect() {
        setMyPhone((prev) => prev + 1);
        setMyPhone((prev) => prev + 1);
        setMyPhone((prev) => prev + 1);
    }

    function increase() {
        setMyPhone((prev) => prev + 1);
    }
}
Child HTML+TypeScript:
import { useState } from "react";

type Params = {
    phoneNumber: string;
    mainOffice: string;
    initialEmployeeCount: number;
    onEmployeeCountChanged: (newCount: number) => void;
};

export function ContactUs({
    phoneNumber,
    mainOffice: primaryOffice,
    initialEmployeeCount,
    onEmployeeCountChanged,
}: Params) {
    const [employeeCount, setEmployeeCount] = useState(initialEmployeeCount);

    return (
        <div>
            <h4>Contact Us</h4>
            Phone: {phoneNumber} <br />
            Main Office: {primaryOffice}<br />
            
            <button onClick={welcomeEmployee}>Welcome employee</button>
        </div>
    );

    function welcomeEmployee() {
        setEmployeeCount((prev) => prev + 1);
        onEmployeeCountChanged(employeeCount);
    }
}
Angular: @Input @OutputReact's Angular @Input @Output

Sunday, June 22, 2025

.NET Middleware

Inline
app.Use(async (context, next) =>
{
    var logger = app.Services.GetRequiredService<ILoggerFactory>()
        .CreateLogger("RequestLogger");

    logger.LogInformation("inline HTTP {Method} {Path}{Query}",
        context.Request.Method,
        context.Request.Path,
        context.Request.QueryString);
    
    await next();
});
Function
app.Use(MyFuncMiddleware);
async Task MyFuncMiddleware(HttpContext context, Func<Task> next)
{
    var logger = app.Services.GetRequiredService<ILoggerFactory>()
        .CreateLogger("RequestLogger");

    logger.LogInformation("func HTTP {Method} {Path}{Query}",
        context.Request.Method,
        context.Request.Path,
        context.Request.QueryString);
    
    await next();
}
HOC
app.Use(MyHocMiddleware());
Func<HttpContext, Func<Task>, Task> MyHocMiddleware()
{
     var logger = app.Services.GetRequiredService<ILoggerFactory>()
         .CreateLogger("RequestLogger");
     
    return async (context, next) =>
    {
        logger.LogInformation("hoc HTTP {Method} {Path}{Query}",
            context.Request.Method,
            context.Request.Path,
            context.Request.QueryString);
    
        await next();
    };
}
Fluent HOC
app.UseMyFluentHocMiddleware();

public static class MyFluentHocMiddlewareExtensions
{
    // Extension method for IApplicationBuilder to register our custom middleware
    public static IApplicationBuilder UseMyFluentHocMiddleware(this IApplicationBuilder builder)
    {
        var logger = builder.ApplicationServices.GetRequiredService<ILoggerFactory>()
          .CreateLogger("RequestLogger");
        
        return builder.Use(async (context, next) => 
        {
            logger.LogInformation("fluent HTTP {Method} {Path}{Query}",
                 context.Request.Method,
                 context.Request.Path,
                 context.Request.QueryString);

             await next();
        });
    }
}
Fluent Class
app.UseMyFluentClassMiddleware();

// Constructor: The RequestDelegate representing the next middleware MUST be the first parameter.
// Other dependencies (like ILogger) are injected by the DI container.

public class MyClassMiddleware(RequestDelegate next, ILogger<MyClassMiddleware> logger)
{
    // Invoke or InvokeAsync method: This is where the actual middleware logic resides.
    // It must return Task and take HttpContext as the first parameter.
    public async Task InvokeAsync(HttpContext context)
    {
        logger.LogInformation("myClass HTTP {Method} {Path}{Query}",
            context.Request.Method,
            context.Request.Path,
            context.Request.QueryString);
        
        await next(context);

        // logger.LogInformation($"[Class Middleware] Outgoing response status: {context.Response.StatusCode}");
    }
}

// Optional: An extension method to make registering the class middleware more fluent
public static class MyClassMiddlewareExtensions
{
    public static IApplicationBuilder UseMyFluentClassMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<MyClassMiddleware>();
    }
}

Monday, April 5, 2021

Leetcode Everyday: 1748. Sum of Unique Elements. Easy

public class Solution {
    public int SumOfUnique(int[] nums) {
        var hs = 
            from n in nums
            group n by n into g
            where g.Count() == 1
            select g.Key;
        
        return hs.Sum();
    }
}
Source: https://leetcode.com/problems/sum-of-unique-elements/submissions/

Monday, March 29, 2021

Leetcode Everyday: 617. Merge Two Binary Trees. Easy

public class Solution {
    public TreeNode MergeTrees(TreeNode root1, TreeNode root2) =>
        root1 != null || root2 != null ? 
            new TreeNode(
                (root1?.val ?? 0) + (root2?.val ?? 0),
                MergeTrees(root1?.left, root2?.left),
                MergeTrees(root1?.right, root2?.right)            
            )
        : 
            null;                        
}

// TODO: optimize without allocating
Source: https://leetcode.com/problems/merge-two-binary-trees/

Saturday, March 20, 2021

Leetcode Everyday: 728. Self Dividing Numbers. Easy

public class Solution {
    public IList<int> SelfDividingNumbers(int left, int right) {
        var list = new List<int>();
        for (var i = left; i <= right; ++i) {
            for (var n = i; n > 0; n /= 10) {
                var toDivide = n % 10;
                if (toDivide == 0 || i % toDivide != 0) {
                    goto goNextNumber;
                }                
            }
            list.Add(i);
            
goNextNumber:;
        }
        return list;
    }
}
Source: https://leetcode.com/problems/self-dividing-numbers/

Tuesday, March 16, 2021

Leetcode Everyday: 1351. Count Negative Numbers in a Sorted Matrix. Easy

public int CountNegatives(int[][] grid) {
    var count = 0;
    foreach (var line in grid) {
        for (var i = 0; i < line.Length; ++i) {
            if (line[i] <0) {
                count += line.Length - i;
                break;
            }
        }
    }
    return count;
}

// TODO: Create a binary search approach
Functional:
public class Solution {
	public int CountNegatives(int[][] grid) => grid.SelectMany(line => line).Count(n => n < 0);    
}
Source: https://leetcode.com/problems/count-negative-numbers-in-a-sorted-matrix/

Sunday, March 14, 2021

Leetcode Everyday: 1464. Maximum Product of Two Elements in an Array. Easy

public class Solution {
    public int MaxProduct(int[] nums) {
        // first and second highest
        var first = 0;
        var second = 0;
        
        foreach (var n in nums) {
            if (n > first) {
                (first, second) = (n, first);
            } else if (n > second) {
                second = n;
            }
        }
        
        return (first - 1) * (second - 1);
    }
}
Functional Programming:
public class Solution {
    public int MaxProduct(int[] nums) =>
        nums.OrderByDescending(n => n).Take(2)
            .Aggregate((a, b) => (a-1) * (b-1));        
}
Source: https://leetcode.com/problems/maximum-product-of-two-elements-in-an-array/

Saturday, March 13, 2021

Wednesday, March 10, 2021

Leetcode Everyday: 1304. Find N Unique Integers Sum up to Zero. Easy

public class Solution {
    public int[] SumZero(int n) {
        var list = new int[n];
         
        var ndx = 0;
        for (var i = 1; i <= n / 2; ++i) {
            list[ndx++] = i;
            list[ndx++] = -i;
        }
         
        if (n % 2 == 1) {
            list[ndx] = 0;
        }
         
        return list;
    }
}
Alternative:
public class Solution {
    public int[] SumZero(int n) {
        var list = new int[n];
         
        for (int i = 1, ndx = 0; i <= n / 2; ++i, ndx += 2) {
            list[ndx] = i;
            list[ndx+1] = -i;
        }
         
        if (n % 2 == 1) {
            list[^1] = 0;
        }
         
        return list;
    }
}
This works, but is confusing:
public class Solution {
    public int[] SumZero(int n) {
        var list = new int[n];
         
        var i = 0;
        while (i < n / 2) {
            list[i++] = i;
            list[^i] = -i;
        }
          
        if (n % 2 == 1) {
            list[i] = 0;
        }
          
        return list;
    }
}
Source: https://leetcode.com/problems/find-n-unique-integers-sum-up-to-zero/submissions/

Leetcode Everyday: 1370. Increasing Decreasing String. Easy

public class Solution {
    public string SortString(string s) {
        var sorted = s.Distinct().OrderBy(x => x).ToList();
        var sortedDesc = sorted.OrderByDescending(x => x).ToList();
        
        var sb = new StringBuilder();
        
        var isAscending = true;
        for (; s.Length > 0; isAscending = !isAscending) { 
            var toSort = isAscending ? sorted : sortedDesc;            
            foreach (var c in toSort) {
                var i = s.IndexOf(c);
                if (i >= 0) {
                    s = s.Remove(i, 1);
                    sb.Append(c);
                }
            }
        }
        
        return sb.ToString();
    }
}
Optimized
public class Solution {
    public string SortString(string s) {
        const int z = 26;
        var freq = new int[z];
        
        var sLength = s.Length;
        var sb = new char[sLength]; // think of this as StringBuilder
        
        foreach (var c in s){
            ++freq[c - 'a'];
        }
        
        for (var i = 0; i < sLength; ){
            
            for (var small = 0; small < z; ++small){
                if (freq[small] > 0){
                    --freq[small];
                    sb[i] = (char)('a' + small);
                    
                    ++i;
                }
            }
            
            for (var large = z - 1; large >= 0; --large){
                if (freq[large] > 0){
                    --freq[large];
                    sb[i] = (char)('a' + large);

                    ++i;
                }
            }
            
        }
        
        return new string(sb);
    }
}
Source: https://leetcode.com/problems/increasing-decreasing-string/submissions/

Monday, March 8, 2021

Leetcode Everyday: 1704. Determine if String Halves Are Alike. Easy

public class Solution {
    public bool HalvesAreAlike(string s) {
        var half = s.Length / 2;
        var a = s.Substring(0, half);
        var b = s.Substring(s.Length - half, half);
        
        var vowels = new HashSet<char>() {'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'};
        
        return a.Count(c => vowels.Any(v => char.ToUpper(c) == v))
            == b.Count(c => vowels.Any(v => char.ToUpper(c) == v));
    }
}
Optimized:
public class Solution {
    public bool HalvesAreAlike(string s) {
        var half = s.Length / 2;        
        var vowels = new HashSet<char>() {'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'};
                
        var count = 0;        
        for (var i = 0; i < half; ++i) {
            if (vowels.Contains(s[i]))
                ++count;
            
            if (vowels.Contains(s[^(i + 1)])) 
                --count;
        }        
        return count == 0;
    }
}
public class Solution {
    public bool HalvesAreAlike(string s) {
        var half = s.Length / 2;        
                
        var count = 0;        
        for (var i = 0; i < half; ++i) {
            if (s[i] switch {
                'A' => true,
                'a' => true,
                'E' => true,
                'e' => true,
                'I' => true,
                'i' => true,
                'O' => true,
                'o' => true,
                'U' => true,
                'u' => true,
                _ => false                    
            }) {
                ++count;
            }
            
            if (s[(^(i + 1))] switch {
                'A' => true,
                'a' => true,
                'E' => true,
                'e' => true,
                'I' => true,
                'i' => true,
                'O' => true,
                'o' => true,
                'U' => true,
                'u' => true,
                _ => false                    
            }) {
                --count; 
            }
        }        
        return count == 0;
    }
}
Source: https://leetcode.com/problems/determine-if-string-halves-are-alike/

Sunday, March 7, 2021

Leetcode Everyday: 1436. Destination City. Easy

public class Solution {
    public string DestCity(IList<IList<string>> paths) {
        var pathLookup = new Dictionary<string, string>();
        
        foreach (var path in paths) {
            pathLookup[path.First()] = path.Last();
        }
        
        var source = pathLookup.FirstOrDefault().Key;
        while (pathLookup.TryGetValue(source, out string destination)) {
            source = destination;
        }   
        
        return source;
    }
}
Functional programming:
public class Solution {
    public string DestCity(IList<IList<string>> paths) =>        
        paths.Single(dest => !paths.Any(source => dest.Last() == source.First())).Last();
}
Optimized:
public class Solution {
    public string DestCity(IList<IList<string>> paths) {
        var outgoing = new HashSet<string>();        
        outgoing.UnionWith(paths.Select(path => path.First()));
        
        foreach (var path in paths) {
            if (!outgoing.Contains(path.Last())) {
                return path.Last();
            }
        }
        
        return null;
    }
}
Source: https://leetcode.com/problems/destination-city/submissions/

Saturday, March 6, 2021

Leetcode Everyday: 1309. Decrypt String from Alphabet to Integer Mapping. Easy

using System.Text.RegularExpressions;

public class Solution {
    public string FreqAlphabets(string s) {
        var lookup = new Dictionary<string, char>();
        
        var rx = new Regex(@"\d{2}#|\d{1}");
        var alphabet = "12345678910#11#12#13#14#15#16#17#18#19#20#21#22#23#24#25#26#";
        
        var c = 'a';
        foreach (var match in rx.Matches(alphabet).Cast<Match>()) {
            lookup[match.Value] = c++;
        }
        
        var sb = new StringBuilder();        
        foreach (var match in rx.Matches(s).Cast<Match>()) {
            sb.Append(lookup[match.Value]);
        }
        
        return sb.ToString();        
    }
}
Optimized:
public class Solution {
    public string FreqAlphabets(string s) {
        var sb = new StringBuilder();
        for (var i = 0; i < s.Length; ) {
            if (i+2 < s.Length && s[i+2] == '#') {
                sb.Append((char)('a' + (s[i]-'0')*10 + s[i+1]-'0' - 1));                
                i += 3;
            } else {        
                sb.Append((char)('a' + s[i]-'0' - 1));
                ++i;
            }
        }
        return sb.ToString();                
    }
}
Source: https://leetcode.com/problems/decrypt-string-from-alphabet-to-integer-mapping/

Thursday, March 4, 2021

Leetcode Everyday: 1725. Number Of Rectangles That Can Form The Largest Square. Easy

public class Solution {
    public int CountGoodRectangles(int[][] rectangles) {
        var minCounts = new Dictionary<int, int>();    
        
        foreach (var r in rectangles) {
            var min = Math.Min(r[0], r[1]);           
            minCounts[min] = 
                (minCounts.TryGetValue(min, out int value) ? value : 0) + 1;        
        }
                            
        return minCounts[minCounts.Keys.Max()];
    }    
}
Source: https://leetcode.com/problems/number-of-rectangles-that-can-form-the-largest-square/

Wednesday, March 3, 2021

Leetcode Everyday: 1323. Maximum 69 Number. Easy

public class Solution {
    public int Maximum69Number (int num) {
        
        var sNum = num.ToString();
        var foundAt = sNum.IndexOf('6');
                   
        if (foundAt >= 0) {
            return num +  (3 * (int)Math.Pow(10, sNum.Length - foundAt - 1));
        }
        
        return num;        
    }
}
Same speed as above (32ms), but uses less memory:
public class Solution {
    public int Maximum69Number (int num) {
    
            var add = 0;

            for (int temp = num, step = 1; temp > 0; temp /= 10, step *= 10) {
                if (temp % 10 == 6) {
                    add = step * 3;
                }
            }

            return num + add;
    }
}
Source: https://leetcode.com/problems/maximum-69-number/