dev
dev

Reputation: 916

find the next path from the list

How can i reduce this logic into much more better one like a reusable hook function, where i can pass the current path and the tabsDetails and it returns me the next path so I can push to the next route once user clicks on next button

 const tabsDetails = [
    {
      path: "/user-details",
    },
    {
      path: "/service-details",
    },
    {
      path: "/previous-experience-details",
    },
    {
      path: "/documents",
    },
  ];

  const allowNextRoute = () => {
    // early return if tabsDetails is having falsy values or length is 0
    if (!tabsDetails?.length) return
    const { pathname } = useLocation() //'details/11012/user-details'
    const currentPathsSplitted = pathname?.split("/") 
    const currentPath = currentPathsSplitted.reverse()[0]
    const filteredPaths = tabsDetails.map(({ path }) => path)
    let currentPathIndex = filteredPaths.indexOf(`/${currentPath}`)
    let nextPathIndex = ++currentPathIndex % filteredPaths.length
    let nextPath = filteredPaths[nextPathIndex]
    currentPathsSplitted.reverse().pop()
    currentPathsSplitted.push(nextPath.split("/").reverse()[0])
    history.push(currentPathsSplitted.join("/")) // 'details/11012/service-details
  }

Upvotes: 2

Views: 124

Answers (2)

3limin4t0r
3limin4t0r

Reputation: 21120

I would personally start with transforming the structure into one that supports the logic you attempt to deploy.

Instead of using an array you might want to transform the current data in a more usable structure that simplifies path lookup and finding the next item. One such structure could be a "linked list" where you have a "node" with the structure:

{ item: item, next: nextNode }

Then use an object to make lookups a specific paths fast:

const tabDetailsLookup = {
  "/user-details": { 
    item: { path: "/user-details" }
    next: -> ref to "/service-details" node
  },
  "/service-details": {
    item: { path: "/service-details" }
    next: -> ref to "/previous-experience-details" node
  },
  // ...
}

You can build this structure using a simple for-loop:

const tabsDetails = [
  { path: "/user-details" },
  { path: "/service-details" },
  { path: "/previous-experience-details" },
  { path: "/documents" },
];

// prepare data structure to fit logic needs
const tabDetailsLookup = (function () {
  // place array elements inside nodes
  const nodes = tabsDetails.map(item => ({ item }));
  const lookup = {};
  
  for (let i = 0; i < nodes.length; ++i) {
    // create a reference from the current node to the next node
    nodes[i].next = nodes[(i + 1) % nodes.length];
    // add the current node to the lookup object
    lookup[nodes[i].item.path] = nodes[i];
  }
  
  return lookup;
})();

console.log(tabDetailsLookup);

// this structure makes it a lot easier to find the next path
console.log(tabDetailsLookup["/user-details"].next.item.path);

This simplifies:

const filteredPaths = tabsDetails.map(({ path }) => path)
let currentPathIndex = filteredPaths.indexOf(`/${currentPath}`)
let nextPathIndex = ++currentPathIndex % filteredPaths.length
let nextPath = filteredPaths[nextPathIndex]

Into:

const nextPath = tabDetailsLookup[`/${currentPath}`].next.item.path;

You can supplement this with a function that does the other stuff:

function nextPath(path) {
  const parts = path.split("/");
  if (parts.length == 0) return;
  const current = parts.pop();
  const node = tabDetailsLookup[`/${current}`];
  if (!node) return;
  return parts.join("/") + node.next.item.path;
}

const tabsDetails = [
  { path: "/user-details" },
  { path: "/service-details" },
  { path: "/previous-experience-details" },
  { path: "/documents" },
];

// prepare data structure to fit logic needs
const tabDetailsLookup = (function () {
  // place array elements inside nodes
  const nodes = tabsDetails.map(item => ({ item }));
  const lookup = {};
  
  for (let i = 0; i < nodes.length; ++i) {
    // create a reference from the current node to the next node
    nodes[i].next = nodes[(i + 1) % nodes.length];
    // add the current node to the lookup object
    lookup[nodes[i].item.path] = nodes[i];
  }
  
  return lookup;
})();

function nextPath(path) {
  const parts = path.split("/");
  if (parts.length == 0) return;
  const current = parts.pop();
  const node = tabDetailsLookup[`/${current}`];
  if (!node) return;
  return parts.join("/") + node.next.item.path;
}

console.log(nextPath("details/11012/user-details"));
console.log(nextPath("details/11012/service-details"));
console.log(nextPath("non-existent"));

Upvotes: 1

Robin Mackenzie
Robin Mackenzie

Reputation: 19319

You can use pop and findIndex to reduce the array gymnastics:

const tabsDetails = [{path: "/user-details"},{path: "/service-details"},{path: "/previous-experience-details"},{path: "/documents"}];
const delim = "/";

const allowNextRoute = (journey, resource) => {
  if (!journey) return;
  const parts = resource.split(delim);
  const current = `${delim}${parts.pop()}`;
  const path = parts.join(delim); 
  const currIndex = journey.findIndex(o => o.path === current);
  const test = (currIndex < journey.length - 1) && (currIndex > -1);
  return test ? `${path}${journey[currIndex + 1].path}` : "";
}

let path;

console.log("Test 1 - OP");
path = "details/11012/user-details"; // useLocation()
while (path) {
  console.log(path);
  path = allowNextRoute(tabsDetails, path);
}

console.log("Test 2 - no matching path");
path = "details/11012/user-details-error"; // useLocation()
while (path) {
  console.log(path);
  path = allowNextRoute(tabsDetails, path);
}

console.log("Test 3 - no details");
path = "details/11012/user-details"; // useLocation()
while (path) {
  console.log(path);
  path = allowNextRoute([], path);
}

console.log("Test 4 - details with no match");
path = "details/11012/user-details"; // useLocation()
while (path) {
  console.log(path);
  path = allowNextRoute([{path: "/foo"}], path);
}

Upvotes: 1

Related Questions