Sort an array of strings with binary search and insert a new string

My current challenge involves working with an alphabetically sorted array of strings:

let collection = ["ABC", "BCD", "CAB", "FGH", "JKL", "ZKL"];

The task at hand is to insert the string "CQW" into the collection while maintaining the sorted order without having to re-sort the entire array.

My desired outcome is to have

["ABC", "BCD", "CAB", "CQW", "FGH", "JKL", "ZKL"];
after the insertion is completed in O(log n) time.

To achieve this, I am considering leveraging binary search to calculate the index at which the new element should be inserted.

I came across a binary search code snippet that can find the index of a string within the collection:

function binarySearch(items, value) {
  let startIndex = 0;
  let stopIndex = items.length - 1;
  let middle = Math.floor((stopIndex + startIndex) / 2);

  while (items[middle] != value && startIndex < stopIndex) {

    //adjust search area
    if (value < items[middle]) {
      stopIndex = middle - 1;
    } else if (value > items[middle]) {
      startIndex = middle + 1;
    }

    //recalculate middle
    middle = Math.floor((stopIndex + startIndex) / 2);
  }

  // Return -1 if element is not in collection
  return (items[middle] != value) ? -1 : middle;
}

let collection = ["ABC", "BCD", "CAB", "FGH", "JKL", "ZKL"];

console.log(binarySearch(collection, "CQW"));

However, I am facing a challenge in modifying the code to return the exact index for inserting the string. How can this code be adjusted to achieve that? Is binary search the most efficient approach for this task?

Answer №1

When inserting into an array, the worst-case scenario is always O(n) due to moving values around after insertion, along with an additional n or log n for finding the insertion point. One approach could be to append the value at one end of the array and then utilize insertion sort, as it performs well on nearly-sorted input data.

const insertionSort = (inputArr) => {
    const length = inputArr.length;
    for (let i = 1; i < length; i++) {
        const key = inputArr[i];
        let j = i - 1;
        while (j >= 0 && inputArr[j] > key) {
            inputArr[j + 1] = inputArr[j];
            j = j - 1;
        }
        inputArr[j + 1] = key;
    }
    return inputArr;
};

let collection = ["ABC", "BCD", "CAB", "FGH", "JKL", "ZKL"];

collection.push("CQW");
console.log(insertionSort(collection));

Alternatively, for scenarios where large arrays require O(n) complexity for insertion, a sorted doubly-linked list may be a better choice.

const linkedList = (value, next) => ({prev: null, value, next});
const insert = (node, value) => {
  if (node === null) {
    return false;
  }
  if (node.value < value) {
    return insert(node.next, value);
  }
  const newNode = linkedList(value, node);
  newNode.prev = node.prev;
  newNode.prev.next = newNode;
  node.prev = newNode;
  return true;
} 
const arrayToList = (arr) => arr.reverse().reduce((next, el) => {
  const list = linkedList(el, next);
  if (next) {
    next.prev = list;
  }
  return list;
}, null);
const printList = (list) => {
  const arr = [];
  let node = list;
  while (node) {
    arr.push(node.value);
    node = node.next;
  }
  console.log(arr);
};


const collection = ["ABC", "BCD", "CAB", "FGH", "JKL", "ZKL"];
const list = arrayToList(collection);
insert(list, "CQW");

printList(list);


// Some function that arent't used in the example
// but are very useful if you decide to use this solution
const get = (list, index) => {
  let node = list;
  for (let i = 0; node; i++) {
    if (i === index) {
      return node.value;
    }
    node = node.next;
  }
  return null;
}
const set = (list, index, value) => {
  let node = list;
  for (let i = 0; node; i++) {
    if (i === index) {
      node.value = value;
      return true;
    }
    node = node.next;
  }
  return false;
}
const remove = (list, value) => {
  if (node === null) {
    return false;
  }
  if (node.value === value) {
    node.prev.next = node.next;
    node.next.prev = node.prev;
    return true;
  }
  return remove(node.next, value);
}
const getIndex = (list, value) => {
  let node = list;
  for (let i = 0; node; i++) {
    if (node.value === value) {
      return i;
    }
    node = node.next;
  }
  return -1;
} 

Answer №2

The middle value indicates the appropriate position. Update the return value of the function to also indicate if the item is already present in the collection.

function binarySearch(items, value) {
  console.log('Searching for '+value)
  let startIndex = 0;
  let stopIndex = items.length - 1;
  let middle = Math.floor((stopIndex + startIndex) / 2);

  while (items[middle] != value && startIndex < stopIndex) {

    //adjust search area
    if (value < items[middle]) {
      stopIndex = middle - 1;
    } else if (value > items[middle]) {
      startIndex = middle + 1;
    }

    //recalculate middle
    middle = Math.floor((stopIndex + startIndex) / 2);
  }

  // Return -1 if element is not in collection
  // return (items[middle] != value) ? -1 : middle;
  return {
      found: items[middle] == value,
      middle: middle
  }
}

let collection = ["ABC", "BCD", "CAB", "FGH", "JKL", "ZKL"];
let item = "CQW"
result= binarySearch(collection, item);
console.log(result)
if(!result.found){
    console.log('Adding '+item+' at index '+result.middle)
    collection.splice(result.middle, 0, item);
}
console.log(collection)

Output

Searching for CQW
{found: false, middle: 3}
Adding CQW at index 3
["ABC", "BCD", "CAB", "CQW", "FGH", "JKL", "ZKL"]

Answer №3

Regrettably, the previous answers provided are not entirely accurate. Due to my limited reputation, I am consolidating all my thoughts and suggestions into this answer.

In summary, the most practical solution is also the simplest:

collection.push(newString);
collection.sort();

If avoiding duplicates is necessary, here is an alternative approach:

if (!collection.includes(newString)) {
    collection.push(newString);
    collection.sort();
}

For scenarios where thousands of new strings need to be inserted in a loop, appending them to the original array first and then sorting at the end is recommended.

Assessment of other (less effective) solutions

After benchmarking various solutions, including my own, it became evident that simplicity prevails in actual usage. The push(), sort() method outperformed the alternatives.

Here's a breakdown of the testing process:

  1. Create a sorted array of 10,000 random 3-character all-caps strings
  2. Execute each proposed solution 10,000 times, inserting 10,000 new random strings of the same type into the sorted array
  3. Analyze the time taken to insert 10,000 additional strings

Ensuring fairness, all methods were configured to reject duplicates.

The insertionSort method by Olian04 ranked last in terms of efficiency. The doubly-linked list method, also by Olian04, offered marginal speed improvements at the expense of increased code complexity. Notably, the binarySearch approach, while significantly faster, did not yield correct results.

Ultimately, the push, sort method emerged as the most efficient solution in practical scenarios. My personal method, which involves a linear scan and Array.prototype.splice(), proved to be moderately faster than push, sort.

Although binary search demonstrated superior speed, practical implementation challenges prevent its widespread recommendation.

In my specific use case of managing sorted arrays for autocomplete suggestions in HTML datalists, the negligible difference in insertion speeds among the proposed solutions renders the decision to opt for simplicity and built-in functions a logical one. Real-world applications rarely necessitate rapid-fire insertion of thousands of elements.

For those seeking an O(log n) search/insertion time, exploring binary search trees is recommended for optimized performance.

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

Determine if the object's value is present

My current JavaScript setup looks like this: var NAMES = []; function INFO(id,first,middle,last){ var newMap = {}; newMap[id] = [first, middle, last]; return newMap ; } Next, I have the following code block: for (var j = 0; j < NUMBER.leng ...

What is the proper way to send a list of lists from Ajax to Flask?

Attempting to send a list of list datatype data from a template using AJAX. Here is the code: Template (JS) var mydata = [['tom', 18, 'new york'], ['jack', 16, 'london']]; var data = new FormData(); mydata.forEach( ...

What is the correct way to incorporate scrollIntoView() using jQuery?

I'm trying to implement a jQuery function that will enable the last reply to scroll into view. This is my current code: function displayLastReply(replies){ replies.each(function() { if($(this).index() < nIniRep || afterReply){ $( ...

Is it preferable to include in the global scope or the local scope?

When it comes to requiring a node module, what is the best approach? Should one declare the module in the global scope for accuracy and clarity, or does declaring it in the local scope make more sense? Consider the following examples: Global: let dns = r ...

Send an array from PHP to jQuery using AJAX, then send the array back from jQuery to PHP

I am facing a dilemma where I am passing an array from PHP to jQuery AJAX using `json_encode` and storing it in an empty array declared in the jQuery script as `var myarr = []`. Later in the same script, I am sending the same array `myarr` back to the PHP ...

Material UI in React: A lengthy typography body necessitates a line break

Currently using the Material UI grid system to create two columns side by side. However, due to the Lorem ipsum text length, the right column is pushed down to a new row. Shortening the text will allow it to be displayed properly in two columns. <Grid c ...

Incorporate innerHTML by appending instead of overwriting

Just a quick question, can we actually add content to a <div id="whatEverId">hello one</div> by utilizing: document.getElementById("whatEverId").innerHTML += " hello two"; Is there a way to INSERT content into the div without replacing it??? ...

Trouble arises when trying to load angular ui-bootstrap using require.js unless I change the name to ui.bootstrap

Code Overview: main.js: require.config({ paths: { 'uiBootstrap': '../lib/bootstrap/angular-bootstrap/ui-bootstrap-tpls.min' }, shim: {***}, priority: ['angular'], deps: ...

"Create a function that allows for the dynamic addition of elements to a

I want to dynamically add more li elements, similar to the first one, by simply clicking a button. Unfortunately, the example provided on jsfiddle is not functioning as intended. document.onload = init; function init(){ document.getElementById('a ...

Executing a secondary API based on the data received from the initial API call

Currently, I am diving into the world of RxJS. In my project, I am dealing with 2 different APIs where I need to fetch data from the first API and then make a call to the second API based on that data. Originally, I implemented this logic using the subscri ...

Struggling to make partial updates to a field within my CRUD update function

Currently, I am working on developing a CRUD application using NodeJS and Express to enhance my backend programming skills. The database being used for this project is MongoDB. This particular project serves as a back office for a shop without any frontend ...

Is there a way to modify the color of my question post-submission?

Within my code, there are numerous queries that need to be addressed. Upon submission, I desire to alter the color of each question to green if the response is correct, or red if it is incorrect. document.getElementById("submit-button").addEventLi ...

Having difficulty retrieving the selected value in JSPDF

I am attempting to convert my HTML page into a PDF format by utilizing the following code: domtoimage.toPng(document.getElementById('PrintForm')) .then(function (blob) { var pdf = new jsPDF('p', &apo ...

For the past two days, there has been an ongoing issue that I just can't seem to figure out when running npm start

After multiple failed attempts, I have exhausted all troubleshooting steps including executing npm clear cache --force, deleting node_modules/ and package-lock.json, followed by running npm install, npm build, and eventually npm run dev. The errors encoun ...

Encountering a TypeError stating that the "option is undefined" error has occurred

Unexpectedly, my dropdown menu that was functioning perfectly fine is now throwing an error. I've made several changes and none of them seem to resolve the issue. Could it be a data type mismatch or am I missing something crucial here? Your insights ...

The useRouter hook from next/router was unable to connect because NextRouter was not activated

import { useRouter } from 'next/router'; const Navbar2 = () => { const router = useRouter(); return ( <nav className={`fixed top-0 w-full px-10 bg-white p-4 transition-all duration-500 ${isVisible ? 'top-0' : 'top-[-1 ...

React unable to properly render Array Map method

I am currently working on a project using ChakraUI and React to display a list of team members as cards. However, I am facing an issue where only the text "Team Members" is being displayed on the website, and not the actual mapped array of team members. Ca ...

How can data be shared between two functional child components in a React application?

I've been researching on Google quite a bit on how to transfer props between functional components, but it seems like there isn't much information available (or maybe I'm not using the right keywords). I don't need Redux or globally st ...

Tips for replacing the values obtained during parsing an XML document

I am working with an XML file that contains category and product information. Here is a snippet of the XML data: <categories> <category id="2" name="Pepsi" > <products> <product id="858" name="7UP" price="24.4900" /> ...

Whenever a new entry is made into the textfield, the onChange feature triggers a reset on the content within the textfield

I'm experiencing an issue while creating a SignUp authentication page with Firebase. Every time I try to input text in the text field, it gets reset automatically. I have been unable to identify the root cause of this problem. It seems to be related t ...