Exploring numerical characters within a sequence of characters

I'm currently working on developing a function that can handle a flat array of string decimal integers representing nodes in a tree. Each period signifies hierarchy within the tree. The goal is to build functions called prevNode and nextNode, which will take three parameters: ids, id, planeLock. If a node does not have a previous or next id, the function will return false. When planeLock is set to true, the navigation will stay on the same level instead of moving to the child nodes (e.g., from 1 to 0.1). This is referred to as navigating within the same plane, i.e., sibling nodes rather than their deepest children.

var ids = [
  '0',
  '0.1',
  '1',
  '2',
  '2.0',
  '2.1',
]
  • prevNode(ids, '0') -> false // no previous node
  • prevNode(ids, '1', true) -> 0 // stays on the same plane with plane lock
  • prevNode(ids, '1') -> 0.1 // previous node in the tree
  • prevNode(ids, '2.0', true) -> false
  • prevNode(ids, '2.0') -> 2 // goes up one node

What would be the best approach to parse these strings for the intended outcomes?

Answer №1

Here is a potential solution:

const determineDepth = (nodeID) => {
  return nodeID.split('.').length;
}

const findSiblingNode = (nodeIDs, targetID, lockLevel, goBackwards) => {
  let index = nodeIDs.indexOf(targetID);
  let level = determineDepth(targetID);

  while (goBackwards ? --index >= 0 : ++index < nodeIDs.length) {
    let currentElement = nodeIDs[index];
    let currentLevel = determineDepth(currentElement);
    if (!lockLevel || currentLevel === level) {
      return currentElement;
    }
    if (currentLevel < level) {
       break;
    }
  }
  return false; 
}

const findPreviousNode = (nodeIDs, targetID, lockLevel) => {
  return findSiblingNode(nodeIDs, targetID, lockLevel, true);
}

const findNextNode = (nodeIDs, targetID, lockLevel) => {
  return findSiblingNode(nodeIDs, targetID, lockLevel, false);
}

Check out the demo. It seems that there's a trade-off between caching all depths (quicker but uses memory) and not doing so (slower). If the array is dynamic and you often need to find insertion points, using memoization is recommended. This way, you won't have to recalculate levels repeatedly upon each insertion.

Answer №2

Implementing a sort algorithm is a solid strategy. However, for added functionality, consider transforming your list of IDs into a tree structure.

function createSortedTree(ids) {
  var tree = {name: "", id: "root", children: {}};
  function insert(tree, elem) {
    if(!tree.children[elem[0]]) {
      tree.children[elem[0]] = {
        id: elem[0],
        children: {},
        parent: tree,
        name: tree.id === "root" ? "" + elem[0] : tree.name + "." + elem[0]
      };
    }
    if(elem.length > 1) insert(tree.children[elem[0]], elem.slice(1));
  }
  for(i in ids) insert(tree, ids[i].split("."));

  function traverse(tree) {
    if(current) {
      current.next = tree;
      tree.prev = current;
    }
    current = tree;

    var children = Object.keys(tree.children)
                         .sort(function(a, b) {if(a < b) return -1; else if(a > b) return 1; else return 0;})
                         .map(function(key) {return tree.children[key]});
    for(i in children) {
      if(i > 0) children[i].prevPlane = children[i-1];
      if(i < children.length - 1) children[i].nextPlane = children[i+1];
      traverse(children[i]);
    }
  }
  var current = null;
  traverse(tree);

  return tree;
}

function getNode(tree, id) {
  if(typeof id === "string") id = id.split(".");
  if(id.length === 0) return tree;
  else return getNode(tree.children[id[0]], id.slice(1));
}

var tree = createSortedTree(["0", "0.1", "1", "2", "2.0", "2.1"])
var node = getNode(tree, "2.0");
console.log(node.prev.name);
console.log(node.next.name);

var node = getNode(tree, "1");
console.log(node.prev.name);
console.log(node.prevPlane.name);

http://jsfiddle.net/jxyqjq3c/

Answer №3

const _ = require('lodash')

function comparePaths (path1, path2) {
  const path1Arr = path1.split('.')
  const path2Arr = path2.split('.')
  const maxLength = Math.max(path1Arr.length, path2Arr.length)
  let index = 0
  while (index < maxLength) {
    if (!path1Arr[index] || +path1Arr[index] < +path2Arr[index]) {
      return -1
    }
    if (!path2Arr[index] || +path1Arr[index] > +path2Arr[index]) {
      return 1
    }
    index++
  }
  return 0
}

function getSubset (idsArray, idValue) {
  return _.filter(idsArray, function (_id) {
    const _idArr = _id.split('.')
    const idArr = idValue.split('.')
    const _choppedId = _.take(_idArr, _idArr.length - 1).join('.')
    const choppedId = _.take(idArr, idArr.length - 1).join('.')
    if (_choppedId === choppedId) return true
    return false
  })
}

function extractMetadata (idsList, targetId) {
  idsList = idsList.sort(comparePaths)
  const idx = idsList.indexOf(targetId)
  const metadata = {}
  metadata.prev = (idsList[idx - 1]) ? idsList[idx - 1] : false
  metadata.next = (idsList[idx + 1]) ? idsList[idx + 1] : false
  const subsetIds = getSubset(idsList, targetId)
  const subIndex = subsetIds.indexOf(targetId)
  metadata.prevSibling = (subsetIds[subIndex - 1]) ? subsetIds[subIndex - 1] : false
  metadata.nextSibling = (subsetIds[subIndex + 1]) ? subsetIds[subIndex + 1] : false
  return metadata
}

const identifiers = [ '0', '1', '2', '3', '0.0.0', '0.0.1', '0.0', '1.0' ]

const result = extractMetadata(identifiers, '1')
console.log(result)

Answer №4

One potential solution is to adjust the implementation of nextNode in a similar manner, reusing most of the function but modifying how the iterator behaves.

function nextItem(list, element, axisLock) {
  var pointer = list.indexOf(element) + 1;

  if (axisLock) {    
    while(   ~pointer 
          && !( element.split('.').length === 1 && list[pointer].split('.').length === 1) 
          && !( element.split('.').length === list[pointer].split('.').length && element.split('.')[0] === list[pointer].split('.')[0] ) )
      pointer++;
    return ~pointer ? list[pointer] : false;
  } else return list[pointer] || false;
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is there a way to incorporate a JavaScript variable into an error message while maintaining a single font ID tag?

Initially, my form displays an error message that functions correctly. I have now encountered a dilemma while attempting to customize this error message based on user input gathered earlier in the form: if (thing is true) { $(this).parents("table:fi ...

Vue Charts.js failing to render charts post data retrieval

Here is an example of a JSON response: [[61,57,34],[1,1,3]]. I would like to use the first array for labels and the second array for data. If I manually set labels and data inside the app, it works fine. For example: labels: ["q", "w", "e"] data: [1, 5, ...

Sorting a multidimensional array B in PHP based on the values order in a one-dimensional array A

Substituting the alphabet in a specific way for better visibility. order case-1 $arr_a = array('C','A','B','D'); order case-2 $arr_a = array('B','A','D','C','E'); ...

Top method for adjusting a multi-dimensional array in PHP

Exploring various methods to modify an array, what is the optimal technique for modifying the given multidimensional array? This array comprises two sub-arrays, namely category 1 and category 2. $arr = array( 'category 1' => array( ...

Retrieving JSON data using Jquery (undefined)

When attempting to retrieve a value from JSON data, I am receiving 'undefined'. $.get( baseURL + 'vacation/show/' + name_surname, function( data ) { alert(data); // returns [{"id":"1","title":"Testas","start":"2015-03-04","end":"20 ...

What could be causing NPM to generate an HTTP Error 400 when trying to publish a package?

My current goal is to release an NPM package named 2680. At the moment, there is no existing package, user, or organization with this specific name. Upon inspection of my package.json, it appears that everything else is in order. Here are all the relevant ...

The YouTube-Search NPM module is producing unexpected outcomes

I recently integrated the youtube-search NPM library with express to fetch the first youtube video based on a song name. app.get("/search", (req, res) => { var search = require("youtube-search"); var SONG = req.query.SONG; var opts = { maxR ...

Switch the class between two elements using a link

Here is the HTML code I am working with: <a href="javascript:"> <img class="remove" src="images/remove.png" /> </a> <div class="content"> <h2>About</h2> <p>We are Sydney Wedding Photographers and have ...

Inheriting from the AudioWorkletProcessor class and incorporating a pre-existing audioNode within the class

My goal is to extend the AudioWorkletProcessor class in order to create a new AudioNode. However, I want to include an existing audioNode, such as a GainNode, within the class - specifically inside the process function. The challenge lies in accessing the ...

Incorporate a "Back" button following the removal of the navigation bar in a Meteor-Ionic

When working on a Meteor-Angular-ionic app, I encountered a situation where I needed to hide the nav-bar in a template to create a full-screen view using the following code: <ion-view hide-nav-bar="true"> However, I then faced the challenge of addi ...

Differences between the application/xml and text/xml MIME types

How can I receive an XML response from a servlet? The servlet returns a content type of "application/xml". When using XmlHttpRequest, I am able to get responseText, but not responseXml. Could this be related to the content type or the request type (I' ...

A guide to activating an input field based on the value of another input field in AngularJs

An AngularJs form requires the user to input the number of hours worked. If the value entered is 0, an additional question should be displayed for the reason why no work was done. <label>Hours worked:</label> <input ng-model="hours" type="n ...

Creating a Nuxt3 sitemap for optimal website indexing

Struggling with an issue here. I'm trying to incorporate sitemap generation into my project and utilizing @nuxtjs/sitemap for the task, but it appears that this package is not compatible with Nuxt3 at present. After scouring the internet, I haven&apos ...

When converting to TypeScript, the error 'express.Router() is not defined' may

Currently, I am in the process of converting my express nodejs project from JavaScript to TypeScript. One of the changes I've made is renaming the file extension and updating 'var' to 'import' for "require()". However, there seems ...

Access the contents of objects during the creation process

I am currently in the process of creating a large object that includes API path variables. The challenge I am facing is the need to frequently modify these API paths for application migration purposes. To address this, I had the idea of consolidating base ...

Learn how to dynamically clear and update source data in jQuery autocomplete for enhanced functionality

Here is a snippet of my jQuery code. In this code, 'year' refers to the form input tag ID, and 'years' is an array variable containing all the year values. I have set this array as the source for autocomplete, which works fine. However, ...

How to make a section header stay at the top of a wrapper container

Currently, I am facing an issue with getting a section header to stay fixed at the top of a wrapper div. The challenge lies in the fact that the wrapper div must have a specified height and overflow set to scroll. I came across this example (jsfiddle) tha ...

Objects within the Prototype in Javascript

While delving into the world of AngularJS, I stumbled upon an interesting revelation - when objects are placed in a prototype object, any instances inheriting from that prototype will alter the prototype's objects upon assignment. For example: funct ...

In either PHP or JavaScript regex, extract the initial letter and the rest of the string as individual values

In my PHP/MySQL code, I have IDs stored in the database with a format of a letter followed by a set of three digits. For example, a123. What I am trying to achieve is splitting these IDs into two variables, $var1 = 'a' and $var2 = '123&apos ...

Can you explain the slow parameter feature in Mocha?

While configuring mochaOpts in Protractor, one of the parameters we define is 'slow'. I'm unsure of the purpose of this parameter. I attempted adjusting its value but did not observe any impact on the test execution time. mochaOpts: { re ...