Determine all sequences within an array using JavaScript

Given an array (with a fixed length) of objects in the following structures:

{type: 'A', value: 1}

or

{type: 'B', text: 'b'}

What is the most efficient method to identify all sequences of objects with type 'A' and return their indices?

For example, consider the following array:

[
  {type: 'A', value: 1}, {type: 'A', value: 2}, {type: 'B', text: 'b1'},
  {type: 'A', value: 11}, {type: 'A', value: 12}, {type: 'A', value: 13},
  {type: 'B', text: 'b2'}, {type: 'A', value: 10}, {type: 'B', text: 'b3'}
]

The expected output would be:

[
  {startIndex: 0, startValue: 1, length: 2},
  {startIndex: 3, startValue: 11, length: 3},
  {startIndex: 7, startValue: 10, length: 1},
]

Instead of using a complex forEach loop with multiple conditions, is there a simpler approach to achieve this task effectively?

Thank you.

Answer №1

To achieve this, you can utilize the reduce method. Create a variable called prev to store the previous item's type. If the current item's type matches your target type, check if the previous item had a different type. If so, add a new object; otherwise, increment the length property.

let data = [{type:'A',value:1},{type:'A',value:2},{type:'B',text:'b1'},{type:'A',value:11},{type:'A',value:12},{type:'A',value:13},{type:'B',text:'b2'},{type:'A',value:10},{type:'B',text:'b3'}],
    prev;

const result = data.reduce((acc, { type, value }, index) => {
  if (type === 'A') {
    if (prev !== type) {
      acc.push({ startIndex: index, startValue: value, length: 1 });
    } else {
      acc[acc.length - 1].length++;
    }
  }

  prev = type;
  return acc;
}, []);

console.log(result)

Answer №2

To create a new group, you can simplify the array and check both the value and type for each element.

var array = [{ type: 'A', value: 1 }, { type: 'A', value: 2 }, { type: 'B', text: 'b1' }, { type: 'A', value: 11 }, { type: 'A', value: 12 }, { type: 'A', value: 13 }, { type: 'B', text: 'b2' }, { type: 'A', value: 10 }, { type: 'B', text: 'b3' }],
    result = array.reduce((r, { type, value }, i, a) => {
        var previous = a[i - 1] || {},
            last = r[r.length - 1];
        if (!isFinite(value) || type !== 'A') return r;
        if (previous.type !== type) {
            r.push({ startIndex: i, startValue: value, length: 1 });
            return r;
        }
        if (value === a[last.startIndex].value + last.length) last.length++;
        return r;
    }, []);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Answer №3

To simplify your code, consider utilizing a forEach loop. It enhances readability and reduces complexity.

let arr = [
  {type: 'A', value: 1}, {type: 'A', value: 2}, {type: 'B', text: 'b1'},
  {type: 'A', value: 11}, {type: 'A', value: 12}, {type: 'A', value: 13},
  {type: 'B', text: 'b2'}, {type: 'A', value: 10}, {type: 'B', text: 'b3'}
]

function findSequences(arr, character) {
  let seq = []

  let seqStarted = false;
  let seqI = 0;

  arr.forEach((el, i) => {
    if (el.type == character) {
      if (seqStarted == true) {
        seq[seqI].length += 1;
      } else {
        seqStarted = true;
        seq.push({
          startIndex: i,
          startValue: el.value,
          length: 1
        });
      }
    } else {
      if (seqStarted) {
        seqStarted = false;
        seqI++;
      }
    }
  })

  return seq;
}

console.log(findSequences(arr, 'A'))

Answer №4

Iterating over an array using a simple for loop is one of the simplest ways to construct your desired answer. This approach has linear complexity because you only traverse the array once. There's no need for overly complex conditions in this scenario.

A straightforward solution might look something like this:

const arr = [
{type: 'A', value: 1}, {type: 'A', value: 2}, {type: 'B', text: 'b1'},
{type: 'A', value: 11}, {type: 'A', value: 12}, {type: 'A', value: 13},
{type: 'B', text: 'b2'}, {type: 'A', value: 10}, {type: 'B', text: 'b3'}
];

const result = [];

for (let [index, el] of arr.entries()) {
if (el.type === 'A') {
// consider the first entry encountered
if (result.length === 0) {
result.push({startIndex: index, length: 1, startValue: el.value});
} else {
const lastSequence = result[result.length - 1];
// check if we are continuing with a sequence
if (lastSequence.startIndex + lastSequence.length === index) {
lastSequence.length += 1;
} else {
// create a new sequence if not continuing
result.push({startIndex: index, length: 1, startValue: el.value});
}
}
}
}

console.log(result);

Answer №5

When examining the given array:

var arr =[
   {type: 'A', value: 1}, {type: 'A', value: 2}, {type: 'B', text: 'b1'},
   {type: 'A', value: 11}, {type: 'A', value: 12}, {type: 'A', value: 13},
   {type: 'B', text: 'b2'}, {type: 'A', value: 10}, {type: 'B', text: 'b3'}
];

arr.reduce((result, current, index) => {
    if(current.type == 'A'){
        if(result.length == 0 || index - result[result.length - 1].length != result[result.length - 1]. startIndex){
            result.push({startIndex: index, startValue: current.value, length: 1})
        }
        else{
           result[result.length - 1].length++
       }
    }
    return result;
}, [])

Answer №6

Instead of using the imperative forEach method, you can opt for a declarative approach like this:

const data = [
  {type: 'A', value: 1}, {type: 'A', value: 2}, {type: 'B', text: 'b1'},
  {type: 'A', value: 11}, {type: 'A', value: 12}, {type: 'A', value: 13},
  {type: 'B', text: 'b2'}, {type: 'A', value: 10}, {type: 'B', text: 'b3'}
];

var filteredData = data.map((item,index) => {
return item.type == 'A' ? {startIndex: index,startValue: item.value} : null
}).filter(element => element).reduce((accumulator,item) => {

if (accumulator.filter(element => element.startIndex + element.length  == item.startIndex).length > 0){
accumulator[accumulator.length-1].length ++;
} else {
item.length = 1;
accumulator.push(item);
}
return accumulator;
},[]);
console.log(filteredData);

Answer №7

Despite the abundance of valuable responses already provided, I felt compelled to contribute an answer that delves into a broader scope of segmenting a list. This code snippet allows for the specification of a segmenter function, which evaluates two items to determine if a new segment should be initialized.

Once these segments are established, reaching the desired final output becomes a straightforward process.

const data = [
  {type: 'A', value: 1}, {type: 'A', value: 2}, {type: 'B', text: 'b1'},
  {type: 'A', value: 11}, {type: 'A', value: 12}, {type: 'A', value: 13},
  {type: 'B', text: 'b2'}, {type: 'A', value: 10}, {type: 'B', text: 'b3'}
]

const segmentBy = segmenter => items => {
  const segmentReducer = (prev = [], curr) => {
    let lastSegment = [];
    let lastItem = null;
    
    try {
      lastSegment = prev[prev.length - 1];
      lastItem = lastSegment[lastSegment.length - 1];
    } catch (e) {
      return [...prev, [curr]];
    }
    const requiresNewSegment = segmenter(lastItem, curr);
    
    if (requiresNewSegment) {
      return [...prev, [curr]];
    }
    
    return [...prev.slice(0, prev.length - 1), [...lastSegment, curr]];
  };
  
  return items.reduce(segmentReducer, []);
};

const addIndex = a => a.map((x, i) => ({...x, index: i}))

const segmentByType = segmentBy((a, b) => a.type !== b.type);
const segments = segmentByType(addIndex(data));

const result = segments
  .map(segment => ({
    startIndex: segment[0].index,
    startValue: segment[0].value || null,
    length: segment.length
  }))
  .filter(x => x.startValue !== null)
  
console.dir(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

Is it possible to execute JavaScript code (using Node.js) through AppleScript on MAC OS?

After downloading Node.js for MAC OS from this link: http://nodejs.org/download/ (http://nodejs.org/dist/v0.10.29/node-v0.10.29.pkg), I needed to execute a JavaScript on Google Chrome. To do this, I utilized the following AppleScript code: do shell script ...

Only one condition is met when using the Javascript if statement with the :checked and .bind methods

I need help creating an Override Button to disable auto-complete for a form using Javascript. I want the div "manualOverrideWarning" to display a warning if the button is selected, but my current function only works the first time the user presses the butt ...

Is it possible to convert a jQuery function into an AngularJS directive?

I am currently working on implementing form validation in AngularJS using a directive to enable or disable the submit button based on the form's validity. Although I have a jQuery function that performs input comparison to prevent duplicate informati ...

transfer data from local array to global variable

Need help with returning array values using console.log(array);, currently it's displaying empty value []. Any tips or suggestions would be greatly appreciated. var array = []; var maxLength = 3; var delay = 250; //Shortened the delay var ticker = {}; ...

Difficulty with Vue Router horizontal scrolling to hash functionality

My goal is to achieve horizontal scrolling of the viewport by using a button wrapped in a router-link that navigates to a vue component. I was successful in achieving this functionality using anchor tags: <a href="#about"> <button cl ...

Creating a circular image in a responsive navigation bar

Currently, I have a chat navigation bar with divs representing different users, each displaying a photo of the user. My goal is to have these photos displayed as perfect circles. To achieve this, I am using padding-bottom and width properties. However, I a ...

Ways to cycle through information within a table using a multi-dimensional array

I am currently facing a challenge with iterating data into a table from a multidimensional array. Here is the specific string I am working with. str = 'stack%splitoverflow,php%splitcodeigniter' My approach involves first splitting the string b ...

Issue encountered with the carousel plugin while transitioning to a different page (utilizing Durandal)

I am currently working on a project using the Durandal SPA template, and I have integrated a carousel element into my page: var compositionComplete = function () { $('.testimonials-carousel').carousel({ namespace: "mr-rotato" // ...

How do you manage dependencies for nested components within Angular2?

Encountering an Issue: browser_adapter.js:76 Error: Cannot resolve all parameters for NestedComponent(undefined). Make sure they all have valid type or annotations. at NoAnnotationError.BaseException [as constructor] Diving Deeper Into the Problem: ...

Strategies for identifying specific children in my particular situation

Here's a snippet of my code: <ul id='nav'> <li><h1 id='unique' class='title'>Topic</h1></li> <li><h1 class='toptitle'>Department</h1></ ...

Replicating DIV element with input field

Once again, I find myself stuck in my attempt to duplicate a DIV and its form elements using jQuery. Button: <div class="addNew" id="addSkill">Add Skill <i class="icon-plus"></i></div> This is the Div and contents that I want to ...

Dynamic AngularJS Content Loader

I am attempting to utilize angularJS in order to dynamically load content from my HTML using ajax calls. My goal is to create a single method that can handle different parameters to specify the ajax/REST uri and display the results in various components. B ...

Display only posts from an array in WordPress

I am working with an array that is generated by ACF. My goal is to display all the posts whose IDs are included in this array. This is the code I have: <?php echo '<pre>'; print_r(get_field('wpisyx')) ; echo '</pre&g ...

Adding a struct to an array of structs in Swift 3

For this specific scenario, a Struct is being utilized: struct Note{ var date: String; var comment: String; } An array with two arrays nested within it is then created, var data = [[Note()],[Contributors()]] The purpose of these two arrays is t ...

Associate a unique identifier string with a randomly generated integer identifier by Agora

For my current web project, I am utilizing a String username as the UID to connect to the channel in an Agora video call. However, I now need to incorporate individual cloud recording by Agora into the project. The challenge lies in the fact that cloud r ...

React hooks - The setState function fails to modify state attributes

I've implemented an event binding on the window object for scroll events. The event gets fired correctly every time I scroll, but I'm facing an issue with my setNavState function (which is actually my setState-function) not updating the state pro ...

Centering Dynamic Text Vertically in Adobe Animate using Javascript

Adobe Animate CC Javascript Can someone share the correct way to vertically center text in a dynamic text box using Javascript within Adobe Animate? This is different from the typical CSS method of centering text vertically in HTML. The following code d ...

Error: Unable to access the 'create' property of an undefined object while utilizing sequelize to register a user and add an entry

Whenever I try to execute this controller, an issue arises indicating a problem with the recognition of the .create method from the model. What is the correct way to import Sequelize in order to utilize it effectively? const db = require("../Models/m_use ...

Is the XMLHttpRequest object closed once the response is received?

Attempting to establish a connection with the server using an XMLHttpRequest object to send data intermittently. The process of creating the object and establishing a connection is as follows: var xhr = new XMLHttpRequest(); xhr.open("post", location, tru ...

Creating an overloaded callable interface using TypeScript

The thread on implementing a callable interface provides some helpful information, but it doesn't fully address my specific query. interface lol { (a: number): (b: number) => string // (a: string): (b: string) => string // overloaded wi ...