Skip to content

Instantly share code, notes, and snippets.

@Munawwar
Created October 12, 2024 13:17
Show Gist options
  • Save Munawwar/4699d371a52b2040afd34fb7fcb8cc5d to your computer and use it in GitHub Desktop.
Save Munawwar/4699d371a52b2040afd34fb7fcb8cc5d to your computer and use it in GitHub Desktop.
preact-iso's URL pattern matching algorithm in various languages
// Run program: go run preact-iso-url-pattern.go
package main
import (
"fmt"
"regexp"
"strings"
)
func preactIsoUrlPatternMatch(urlStr, route string) bool {
url := filterEmpty(strings.Split(urlStr, "/"))
routeParts := filterEmpty(strings.Split(route, "/"))
for i := 0; i < max(len(url), len(routeParts)); i++ {
var m, param, flag string
if i < len(routeParts) {
re := regexp.MustCompile(`^(:?)(.*?)([+*?]?)$`)
matches := re.FindStringSubmatch(routeParts[i])
if len(matches) > 3 {
m, param, flag = matches[1], matches[2], matches[3]
}
}
var val string
if i < len(url) {
val = url[i]
}
// segment match:
if m == "" && param == val {
continue
}
// /foo/* match
if m == "" && val != "" && flag == "*" {
break
}
// segment mismatch / missing required field:
if m == "" || (val == "" && flag != "?" && flag != "*") {
return false
}
rest := flag == "+" || flag == "*"
if rest {
break
}
}
return true
}
func filterEmpty(s []string) []string {
var result []string
for _, str := range s {
if str != "" {
result = append(result, str)
}
}
return result
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
// Example usage:
func main() {
fmt.Println(preactIsoUrlPatternMatch("/foo/bar", "/foo/:param"))
fmt.Println(preactIsoUrlPatternMatch("/foo/bar/baz", "/foo/*"))
fmt.Println(preactIsoUrlPatternMatch("/foo", "/foo/:param?"))
fmt.Println(preactIsoUrlPatternMatch("/foo/bar", "/bar/:param"))
fmt.Println(preactIsoUrlPatternMatch("/users/test%40example.com/posts", "/users/:userId/posts"))
}
<?php
// Run program: php preact-iso-url-pattern.php
function preactIsoUrlPatternMatch($url, $route) {
$url = array_filter(explode('/', $url));
$route = array_filter(explode('/', $route ?? ''));
for ($i = 0; $i < max(count($url), count($route)); $i++) {
preg_match('/^(:?)(.*?)([+*?]?)$/', $route[$i] ?? '', $matches);
$m = $matches[1] ?? '';
$param = $matches[2] ?? '';
$flag = $matches[3] ?? '';
$val = $url[$i] ?? null;
// segment match:
if (!$m && $param == $val) continue;
// /foo/* match
if (!$m && $val && $flag == '*') {
break;
}
// segment mismatch / missing required field:
if (!$m || (!$val && $flag != '?' && $flag != '*')) return false;
$rest = $flag == '+' || $flag == '*';
if ($rest) break;
}
return true;
}
// Example usage:
var_dump(preactIsoUrlPatternMatch("/foo/bar", "/foo/:param"));
var_dump(preactIsoUrlPatternMatch("/foo/bar/baz", "/foo/*"));
var_dump(preactIsoUrlPatternMatch("/foo", "/foo/:param?"));
var_dump(preactIsoUrlPatternMatch("/foo/bar", "/bar/:param"));
var_dump(preactIsoUrlPatternMatch('/users/test%40example.com/posts', '/users/:userId/posts'));
?>
# Run program: python3 preact-iso-url-pattern.py
def preact_iso_url_pattern_match(url, route):
url = list(filter(None, url.split('/')))
route = list(filter(None, (route or '').split('/')))
for i in range(max(len(url), len(route))):
m, param, flag = '', '', ''
if i < len(route):
parts = route[i].split(':')
m = ':' if len(parts) > 1 else ''
param = parts[-1]
flag = ''
if param and param[-1] in '+*?':
flag = param[-1]
param = param[:-1]
val = url[i] if i < len(url) else None
# segment match:
if not m and param == val:
continue
# /foo/* match
if not m and val and flag == '*':
break
# segment mismatch / missing required field:
if not m or (not val and flag != '?' and flag != '*'):
return False
rest = flag in ('+', '*')
if rest:
break
return True
# Example usage:
print(preact_iso_url_pattern_match("/foo/bar", "/foo/:param"))
print(preact_iso_url_pattern_match("/foo/bar/baz", "/foo/*"))
print(preact_iso_url_pattern_match("/foo", "/foo/:param?"))
print(preact_iso_url_pattern_match("/foo/bar", "/bar/:param"))
print(preact_iso_url_pattern_match('/users/test%40example.com/posts', '/users/:userId/posts'))
# Run program: ruby preact-iso-url-pattern.rb
def preact_iso_url_pattern_match(url, route)
url = url.split('/').reject(&:empty?)
route = (route || '').split('/').reject(&:empty?)
(0...[url.length, route.length].max).each do |i|
m, param, flag = route[i]&.match(/^(:?)(.*?)([+*?]?)$/)&.captures || ['', '', '']
val = url[i]
# segment match:
next if m.empty? && param == val
# /foo/* match
break if m.empty? && val && flag == '*'
# segment mismatch / missing required field:
return false if m.empty? || (!val && flag != '?' && flag != '*')
rest = flag == '+' || flag == '*'
break if rest
end
true
end
# Example usage:
puts preact_iso_url_pattern_match("/foo/bar", "/foo/:param")
puts preact_iso_url_pattern_match("/foo/bar/baz", "/foo/*")
puts preact_iso_url_pattern_match("/foo", "/foo/:param?")
puts preact_iso_url_pattern_match("/foo/bar", "/bar/:param")
puts preact_iso_url_pattern_match('/users/test%40example.com/posts', '/users/:userId/posts')
// Run program: javac PreactIsoUrlPattern.java && java PreactIsoUrlPattern
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class PreactIsoUrlPattern {
public static boolean match(String url, String route) {
List<String> urlParts = Arrays.stream(url.split("/"))
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
List<String> routeParts = Arrays.stream((route != null ? route : "").split("/"))
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
Pattern pattern = Pattern.compile("^(:?)(.*?)([+*?]?)$");
for (int i = 0; i < Math.max(urlParts.size(), routeParts.size()); i++) {
String val = i < urlParts.size() ? urlParts.get(i) : null;
String routePart = i < routeParts.size() ? routeParts.get(i) : "";
Matcher matcher = pattern.matcher(routePart);
String m = "", param = "", flag = "";
if (matcher.find()) {
m = matcher.group(1);
param = matcher.group(2);
flag = matcher.group(3);
}
// segment match:
if (m.isEmpty() && param.equals(val)) continue;
// /foo/* match
if (m.isEmpty() && val != null && flag.equals("*")) {
break;
}
// segment mismatch / missing required field:
if (m.isEmpty() || (val == null && !flag.equals("?") && !flag.equals("*"))) return false;
boolean rest = flag.equals("+") || flag.equals("*");
if (rest) break;
}
return true;
}
public static void main(String[] args) {
// Example usage
System.out.println(match("/foo/bar", "/foo/:param"));
System.out.println(match("/foo/bar/baz", "/foo/*"));
System.out.println(match("/foo", "/foo/:param?"));
System.out.println(match("/foo/bar", "/bar/:param"));
System.out.println(match("/users/test%40example.com/posts", "/users/:userId/posts"));
}
}
// Run program:
// dotnet new console -n PreactIsoUrlPattern
// cd PreactIsoUrlPattern
// cp ../Program.cs .
// dotnet run
using System;
using System.Linq;
using System.Text.RegularExpressions;
class Program
{
static bool preactIsoUrlPatternMatch(string url, string route)
{
var urlParts = url.Split('/').Where(s => !string.IsNullOrEmpty(s)).ToArray();
var routeParts = (route ?? "").Split('/').Where(s => !string.IsNullOrEmpty(s)).ToArray();
for (int i = 0; i < Math.Max(urlParts.Length, routeParts.Length); i++)
{
var match = Regex.Match(i < routeParts.Length ? routeParts[i] : "", @"^(:?)(.*?)([+*?]?)$");
var m = match.Groups[1].Value;
var param = match.Groups[2].Value;
var flag = match.Groups[3].Value;
var val = i < urlParts.Length ? urlParts[i] : null;
// segment match:
if (string.IsNullOrEmpty(m) && param == val) continue;
// /foo/* match
if (string.IsNullOrEmpty(m) && val != null && flag == "*")
{
break;
}
// segment mismatch / missing required field:
if (string.IsNullOrEmpty(m) || (val == null && flag != "?" && flag != "*")) return false;
bool rest = flag == "+" || flag == "*";
if (rest) break;
}
return true;
}
static void Main(string[] args)
{
Console.WriteLine(preactIsoUrlPatternMatch("/foo/bar", "/foo/:param"));
Console.WriteLine(preactIsoUrlPatternMatch("/foo/bar/baz", "/foo/*"));
Console.WriteLine(preactIsoUrlPatternMatch("/foo", "/foo/:param?"));
Console.WriteLine(preactIsoUrlPatternMatch("/foo/bar", "/bar/:param"));
Console.WriteLine(preactIsoUrlPatternMatch("/users/test%40example.com/posts", "/users/:userId/posts"));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment