Skip to content

Instantly share code, notes, and snippets.

@vbfox
Last active March 21, 2023 07:31
Show Gist options
  • Save vbfox/384b219560ad625be643 to your computer and use it in GitHub Desktop.
Save vbfox/384b219560ad625be643 to your computer and use it in GitHub Desktop.
Make a path relative in C# and F#
using System;
using System.IO;
static class RelativePath
{
private static string[] GetPathPart(string path)
{
return path
.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
}
public static string Create(string root, string path)
{
var pathParts = GetPathPart(path);
var rootParts = GetPathPart(root);
var diffIndex = pathParts
.Zip(rootParts, (p, r) => new { PathPart = p, RootPart = r })
.Select((x, index) => new { x.PathPart, x.RootPart, Index = index })
.Aggregate(-1, (diff, x) =>
{
var noDifferenceFound = diff == -1;
var currentDifferent = x.PathPart != x.RootPart;
if (noDifferenceFound && currentDifferent)
{
return x.Index;
}
else
{
return diff;
}
});
string[] finalParts;
if (diffIndex == 0)
{
finalParts = pathParts;
}
else
{
var toSkip = diffIndex == -1 ? rootParts.Length : diffIndex;
var backTracks = rootParts.Length - toSkip;
finalParts = Enumerable.Repeat("..", backTracks).Concat(pathParts.Skip(toSkip)).ToArray();
}
return string.Join(Path.DirectorySeparatorChar.ToString(), finalParts);
}
}
module Utils =
open System
open System.IO
let createRelativePath (root: string) (path: string) =
let getPathParts (p:string) =
p.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
let pathParts = getPathParts path
let rootParts = getPathParts root
let diffIndex =
pathParts
|> Seq.zip rootParts
|> Seq.mapi(fun i x -> (i, x))
|> Seq.fold (fun diff (i, (p, r)) ->
match (diff = -1, p <> r) with
| (true, true) -> i
| _ -> diff
) -1
let finalParts =
match diffIndex with
| 0 -> pathParts |> Seq.ofArray
| diffIndex ->
let toSkip = if diffIndex = -1 then rootParts.Length else diffIndex
let backTracks = rootParts.Length - toSkip
Seq.concat [ Seq.replicate backTracks ".."; pathParts |> Seq.skip toSkip ]
String.Join(Path.DirectorySeparatorChar.ToString(), finalParts)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment