{"id":24193700,"url":"https://github.com/ftzi/fractional-indexing-utils","last_synced_at":"2026-01-31T16:02:19.561Z","repository":{"id":115671297,"uuid":"420582067","full_name":"ftzi/fractional-indexing-utils","owner":"ftzi","description":"Common functionalities when using fractional indexing","archived":false,"fork":false,"pushed_at":"2021-10-24T04:45:02.000Z","size":16,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-08T17:41:47.267Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ftzi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-10-24T03:59:33.000Z","updated_at":"2024-04-11T01:07:21.000Z","dependencies_parsed_at":null,"dependency_job_id":"2e9026f6-4145-49b8-977e-801bee8ab4b9","html_url":"https://github.com/ftzi/fractional-indexing-utils","commit_stats":null,"previous_names":["hfantauzzi/fractional-indexing-utils","brfantauzzi/fractional-indexing-utils","ftzi/fractional-indexing-utils"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ftzi/fractional-indexing-utils","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ftzi%2Ffractional-indexing-utils","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ftzi%2Ffractional-indexing-utils/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ftzi%2Ffractional-indexing-utils/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ftzi%2Ffractional-indexing-utils/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ftzi","download_url":"https://codeload.github.com/ftzi/fractional-indexing-utils/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ftzi%2Ffractional-indexing-utils/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28947567,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-31T14:26:55.697Z","status":"ssl_error","status_checked_at":"2026-01-31T14:26:52.545Z","response_time":128,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-13T17:45:20.020Z","updated_at":"2026-01-31T16:02:19.544Z","avatar_url":"https://github.com/ftzi.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# fractional-indexing-utils\nCommon functionalities when using [fractional-indexing](https://github.com/rocicorp/fractional-indexing) npm package. Typescript supported.\n\nSome weeks ago I had the necessity of allowing users to reorder their nested draggable lists, containing categories and items. I had basically the idea of creating the same as the fractional-indexing did, before I knew the already existence of it. As I am in a startup rush and time isn't my friend, I searched for dozens of pages at npm, in the hope that someone else had this idea before me and did implement it. In this case, someone had the idea and someone else implemented it.\n\nWhile the fractional-indexing lib works, it lacks some built-in functionalities that I reused some times in my client and in my server, and certainly people that uses the fractional-indexing had to the something similar to what I've done.\n\nFor now, I will just drop its code here as I haven't befriended time yet. If you, fellow reader, found this somehow and want a npm package, open an issue so I feel more motivated to do it! I created this repo now because commonly I come to useful and reusable code but they never leave my programs. This is for now the middle ground between not publicizing at all and creating a npm package lol. Not really too pretty rn, but this won't be popular at all.\n\nIt assumes your FI (fractional indexed) objects has the property 'sortId', containing the fractional-indexing generated key, a pattern that I came up with. It could be a dynamic key, but for now it suits me well.\n\nWhen you want nested data, like categories and items, I suggest you to have them both as props in an object at the same level, and in the item, have the categoryId. This way you can easily change the item category and its order, without changing anything else. Smart! I had come up to this resolution because I am now using Firebase Firestore, and when both are in a single a doc, I can just deep update() the specific category/item with {['path.to.item']: 'notation'}.\n\n\n```ts\nimport { generateKeyBetween } from 'fractional-indexing';\n\n\ntype obj = Record\u003cstring, unknown\u003e;\n\ntype FiObjBase = {sortId: string};\n\n\n/** Simple alphabetical ordering, how FI is designed to be sorted. */\nfunction sortBySortId(a: FiObjBase, b: FiObjBase): number {\n  if (a.sortId \u003c b.sortId)\n    return -1;\n  if (a.sortId \u003e b.sortId)\n    return 1;\n  return 0;\n}\n\n\n/** Returns a sorted array from a FI record. */\nexport function arrayFromFiRecord\u003cT extends FiObjBase\u003e(fiRecord: Record\u003cstring, T\u003e): T[] {\n  return Object.values(fiRecord).sort(sortBySortId);\n}\n\n\nexport type ObjWithItsId\u003cObj extends obj\u003e = Obj \u0026 {_id: string};\n\n\ntype NestedFiRecord\u003cA extends obj, B extends obj, KeyChildren extends string\u003e = ObjWithItsId\u003c{\n  [k in KeyChildren]: (ObjWithItsId\u003cB\u003e)[]\n}\u003e \u0026 A;\n\n\n/** Constructs an array of arrays, from two FI objects, one that links to another one.\n * \n * Both are FI-ordered. */\nexport function arrayFromNestedFiRecord\u003c\n  A extends FiObjBase,\n  B extends FiObjBase \u0026 {[k in BToAProp]: string},\n  BToAProp extends keyof B,\n  KeyChildren extends string,\n\u003e(\n  aFiRecord: Record\u003cstring, A\u003e,\n  bFiRecord: Record\u003cstring, B\u003e,\n  { bToAProp, keyChildren = '_children' as KeyChildren }: {\n    /** What prop links 'b' to its parent 'a' */\n    bToAProp: BToAProp;\n    /** The prop in a that will contain b. */\n    keyChildren?: KeyChildren;\n  },\n): NestedFiRecord\u003cA, B, KeyChildren\u003e[] {\n  const record: Record\u003cstring, NestedFiRecord\u003cA, B, KeyChildren\u003e\u003e = {};\n  Object.entries(aFiRecord).forEach(([aId, aItem]) =\u003e {record[aId] = { ...aItem, _id: aId, [keyChildren]: [] } as NestedFiRecord\u003cA, B, KeyChildren\u003e;});\n  Object.entries(bFiRecord).forEach(([bId, bItem]) =\u003e {\n    const aItem = record[bItem[bToAProp]];\n    if (aItem)\n      aItem[keyChildren].push({ ...bItem, _id: bId });\n  });\n  return Object.values(record).map((aItem) =\u003e ({\n    ...aItem,\n    [keyChildren]: aItem[keyChildren].sort(sortBySortId),\n  })).sort(sortBySortId);\n}\n\n\n\nexport function getSortIdForIndex(fiArray: FiObjBase[], index: number, currentSortId?: string): string;\nexport function getSortIdForIndex(fiRecord: Record\u003cstring, FiObjBase\u003e, index: number, currentSortId?: string): string;\n/** From an array or a record of FI objects, it returns the sortId of the new item you want to add, at the index you want.\n * \n * You may also \"move\" the item to a new index, by getting its new sortId. To do so,\n * enter the currentSortId of the item in question and its new index. */\nexport function getSortIdForIndex(data: (FiObjBase[] | Record\u003cstring, FiObjBase\u003e), index: number, currentSortId?: string): string {\n  if (typeof data === 'object')\n    data = arrayFromFiRecord(data as Record\u003cstring, FiObjBase\u003e);\n\n  index = Math.min(index, data.length);\n  index = Math.max(0, index);\n  if (currentSortId)\n    data = data.filter((i) =\u003e i.sortId !== currentSortId);\n  // [0,1,2,3,4,5] - if I want to place between 1 and 2, the index would be 2. generateKeyBetween(a[1],a[2]), (index-1, index)\n  // [0, 1] - if I want to move 0 after 1 (index 2), generateKeyBetween(a[1],null). We need to first remove the old position!\n  return generateKeyBetween(data[index - 1]?.sortId ?? null, data[index]?.sortId ?? null);\n}\n\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fftzi%2Ffractional-indexing-utils","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fftzi%2Ffractional-indexing-utils","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fftzi%2Ffractional-indexing-utils/lists"}