What is the best way to compare consecutive string elements within an array using JavaScript?

Given an array of words, I want to identify pairs of opposite directions and remove them from the array, returning a new modified array.

For instance:

let words = ["up", "right", "left", "down", "left", "down", "up", "left"]

should result in:

newWords = ["left", "left"]

Here's my current approach. However, I am encountering an issue where it gives undefined.

let words = ["up", "right", "left", "down", "left", "down", "up", "left"]

function checkOpposite(pair) {
  let upDown = ["up", "down"]
  let downUp = ["down", "up"]
  let rightLeft = ["right", "left"]
  let leftRight = ["left", "right"]
  
  for (var i = 0; i < pair.length - 1; i++) {
    if (pair[i] + pair[i + 1] === upDown) 
    if (pair[i] + pair[i + 1] === downUp)
    if (pair[i] + pair[i + 1] === rightLeft)
    if (pair[i] + pair[i + 1] === leftRight) {
      return true
    }
  }
}

function filterDirections(words) {
  words.filter(checkOpposite)
}

Answer №1

To achieve a stable array, filter it until the size remains constant.

let
    array = ["N", "E", "W", "S", "W", "S", "N", "W"],
    temp;

do {
    let index = -1;
    temp = array;
    array = temp.filter((v, i, { [i + 1]: next }) => {
        if (i === index) return false;
        if (
            v === 'N' && next === 'S' || v === 'S' && next === 'N' ||
            v === 'E' && next === 'W' || v === 'W' && next === 'E'
        ) {
            index = i + 1;
            return false;
        }
        return true;
    });
} while (temp.length !== array.length)

console.log(array);

Answer №2

function eliminateDirections(array) {
    var nCount = 0, eCount = 0, sCount = 0, wCount = 0;
    var result = "";

    for (var direction of array) {
        switch (direction) {
            case "N":
                nCount++;
                break;
            case "E":
                eCount++;
                break;
            case "S":
                sCount++;
                break;
            case "W":
                wCount++;
                break;
        }
    }

    if (eCount > wCount) { eCount -= wCount; wCount = 0; }
    else { wCount -= eCount; eCount = 0; }

    if (nCount > sCount) { nCount -= sCount; sCount = 0; }
    else { sCount -= nCount; nCount = 0; }

    return result + "N".repeat(nCount) + "S".repeat(sCount) + "E".repeat(eCount) + "W".repeat(wCount);
}

let directions = ["N", "E", "W", "S", "W", "S", "N", "W"];
console.log(eliminateDirections(directions));

Insight: By keeping tally of each direction and eliminating opposite pairs, the function efficiently reduces the list of directions. The time complexity is O(n), but further exploration may reveal a faster solution.

Answer №3

One of the main issues is that you are receiving undefined because there is no return statement in your code.

function newDirections(arr) { arr.filter(checkForOpposites) } // <-- no return so undefined

Another issue lies in the logic of your code, as it implies that every element must be equal to each other which is not achievable. The current structure of your code is as follows:

if (arr[i] + arr[i + 1] === nOpp) {
  if (arr[i] + arr[i + 1] === sOpp) {
    if (arr[i] + arr[i + 1] === eOpp) {
      if (arr[i] + arr[i + 1] === wOpp) {
        return true;
      }
    }
  }
}

Moreover, using filter results in multiple loops and internal looping within it which is not efficient. Consider utilizing a loop for a more effective approach.

var opposites = {
  N: 'S',
  S: 'N',
  E: 'W',
  W: 'E',
};

function newDirections(orgArr) {
  var arr = orgArr.slice();
  var i = 0;
  while (i < arr.length - 1) {
    var dir = arr[i];
    var nextDir = arr[i + 1];
    if (opposites[dir] === nextDir) {
      arr.splice(i, 2); 
      if (i > 0) {
        i--;
      }
    } else {
      i++;
    }
  }
  return arr;
}

const arr = ["N", "E", "W", "S", "W", "S", "N", "W"];
console.log(newDirections(arr));

console.log(newDirections(["N", "S", "E", "W", "W", "S"]));
console.log(newDirections(["E", "N", "S", "E", "W", "W", "S"]));
console.log(newDirections(["E", "E", "W", "N", "S", "E", "W", "W"]));

Answer №4

To convert directions into vectors, add the vectors together, and then transform the resulting vector back into a list of directions:

var arr = ["N", "E", "W", "S", "W", "S", "N", "W"]

var vecMap = {
    N: [1,0],
    E: [0,1],
    S: [-1,0],
    W: [0,-1]
};

var revMap = [
    ['N','S'],
    ['E','W']
];

const dir2Vec = dir => vecMap[dir];
const vec2Dir = (acc, cur, i) => {
    // Determine the direction based on the reverse map
    const dir = revMap[i][+(cur < 0)];
    // Create an array with n items where n is the length of the vector
    const dup = Array.from({length: Math.abs(cur)}).fill(dir);
    return [...acc, ...dup]
}
const sumVec = (a,b) => [a[0]+b[0], a[1]+b[1]];

const res = arr
.map(dir2Vec)
.reduce(sumVec)
.reduce(vec2Dir, [])

console.log(res)

Answer №5

Comparing arrays using

if (arr[i] + arr[i + 1] === nOpp)
is not the correct way. Using === will compare if both sides are Array type objects, not their content. To compare array contents, use other methods.

To test this in node.js CLI, run this code:

const nOpp = ["N", "S"];
console.log(nOpp === ["N", "S"]);

You'll see false printed in the console.

For more info, refer to: How to compare arrays in JavaScript?

Nesting if statements like this is equivalent to successive AND conditions:

    if (arr[i] + arr[i + 1] === nOpp) 
    if (arr[i] + arr[i + 1] === sOpp)
    if (arr[i] + arr[i + 1] === eOpp)
    if (arr[i] + arr[i + 1] === wOpp) {
     ...
    }

This means each subsequent if is only checked if the previous one is true.

My revised version simplifies the logic based on your initial question and desired outcome. Here's the code snippet:

let arr = ["N", "E", "W", "S", "W", "S", "N", "W"]
const opposites = [
  ["N", "S"],
  ["W", "E"],
];

const checkForOpposites = data => {
  const result = [];
  data.forEach((v0, i, a) => {
    let oppositeFound = false;
    opposites.forEach(o => {
      if (v0 != a[i+1] && o.includes(v0) && o.includes(a[i+1])) {
        oppositeFound = true
        result.push(i, i+1);
      }
    });
  });
  return result;
}

const cleanOpposites = data => {
  let result = data;
  let n = [];
  do {
    n = checkForOpposites(result);
    if (n.length > 0) {
      result = result.reduce((accum, curr, i) => {
        if (!n.includes(i)) {
          accum.push(curr);
        }
        return accum;
      }, []);
    }
  } while (n.length > 0);
  
  return result;
}

const r0 = cleanOpposites(arr);
console.log(arr);
console.log(r0);

The cleaning process must be recursive as shown above. This approach ensures unwanted elements are removed iteratively until no further matches are found.

Though the code can be optimized, this gives you an idea of how to achieve the desired result.

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

Modify the tooltip of the selected item in an ng-repeat loop in AngularJS

Upon clicking an element, a function is executed which, upon successful completion, should change the tooltip of that specific element. Within an ngRepeat loop, I have multiple elements displaying the same tooltip. However, I only want to update the toolt ...

Having trouble simulating a custom Axios Class in JavaScript/TypeScript

Here are the function snippets that I need to test using jest, but they require mocking axios. My attempt at doing this is shown below: // TODO - mock axios class instance for skipped Test suites describe("dateFilters()", () => { beforeEac ...

Refreshing an iFrame in NextJS from a different component

I am working on a NextJS application that includes a specific page structure: Page: import Layout from "@/components/app/Layout"; import Sidebar from "@/components/app/Sidebar"; export default function SiteIndex() { return ( < ...

Vue - Triggering @change event for file input element when files are set programmatically using ref instead of drag and drop upload

My setup includes a file input for drag and drop image uploads. <input type="file" ref="FileInput" style="display: none;" @change="onFileSelect" accept=". ...

Dealing with the state of an array of checkboxes: What you need to know

Is there a way to individually control the checked state of an array of checkboxes? Here is the array in question: const CheckboxItems = t => [ { checked: true, value: 'itemsCancelled', id: 'checkBoxItemsCancelled', ...

"Exploring the power of asynchronous operations in NodeJS using Q

Currently, I am utilizing Node.js and I aim to incorporate promises to ensure a complete response following a for loop. exports.getAlerts = function(req,res,next){ var detected_beacons = []; if (!req.body || Object.keys(req.body).length == 0) { res.s ...

How to delete an element from an array using PHP

Dealing with a unique issue here - my code is generating an unwanted item in the array labeled "*items" and I'm struggling to remove it. The presence of this item is causing a blank space in my application's display of array data, which is not a ...

Character count in textarea does not work properly when the page is first loaded

As English is not my first language, I apologize in advance for any grammar mistakes. I have implemented a JavaScript function to count the characters in a textarea. The code works perfectly - it displays the character limit reducing as you type. However, ...

Set checkbox variable to undefined at all times

I am having trouble determining the state (True/False/null, etc.) of my "CSS-only" toggle switch. No matter what I try, I keep getting a null value. I'm not sure why this is happening, but it seems that the checkbox input is not functioning as expecte ...

Organize the array of objects

Within my React state, I aim to rearrange a set of 3 objects by always placing the selected one in the middle while maintaining ascending order for the others. Currently, I utilize an order property within each object as a way to keep track of the sequenc ...

Using PHP, assign a reference to a class member

Trying to create a JSON server response in PHP has been quite the task. Using an array in my output to monitor errors and successes within the script has been helpful. Take a look at the code snippet below for a better understanding. <?php $output = ar ...

The concept of strict aliasing and the implementation of flexible array members

Despite my familiarity with C, I find myself puzzled by the following code: typedef struct { int type; } cmd_t; typedef struct { int size; char data[]; } pkt_t; int func(pkt_t *b) { int *typep; char *ptr; /* #1: Triggers a warni ...

Determining the browser width's pixel value to enhance responsiveness in design

Lately, I've been delving into the world of responsive design and trying to grasp the most effective strategies. From what I've gathered, focusing on content-driven breakpoints rather than device-specific ones is key. One thing that would greatl ...

"Using Node.js Express 4.4 to efficiently upload files and store them in a dynamically

Looking for recommendations on the most efficient method to upload a file in node.js using express 4.4.1 and save it to a dynamically created directory? For example, like this: /uploads/john/image.png, uploads/mike/2.png ...

Maintain the state of the previous page in Vue to ensure continuity

Currently in the process of creating a small PWA to simulate an Android app, I have discovered Vuejs. However, I encountered an issue that has proven difficult to resolve. While scrolling through lists on the homepage for movies, TV shows, or news, clicki ...

Unusual behavior observed with AngularJS checkboxes

Our group of friends is collaborating on a new project. This is my second time working with AngularJS, and we're currently utilizing version 1.2.3. Although I have some experience, there are moments when I find AngularJS's behavior perplexing, a ...

Issue with AngularJS: Dynamically generated tab does not become active or selected

Exploring an AngularJS code snippet that generates tabs upon clicking the new button. However, there's an issue where the newly created tab doesn't become active or selected automatically after creation. It seems like the one before the last tab ...

Storing deeply nested arrays of objects with Mongoose leads to MongoDB storing empty objects

Here's the issue I'm facing: I am trying to save nested objects with mongoose in mongodb, but when I save them, the nested objects end up empty. I have attempted various solutions found online, such as pushing objects and populating, but none o ...

Utilize Quasar CSS breakpoints within Vue expressions for responsive design

I am currently using the QDialog component from Quasar Framework and I want to set the value of the maximized property based on the current screen size, specifically being maximized for small screens only. Is there a way for me to reference a variable in ...

Switch the array's value if the key is a match?

I'm currently facing an issue where my code does not push the object when the key matches. How can I update the value of the key instead when there is a match? this.state.data.concat(items).filter(function (a) { return !this[a.key] && (th ...