简体   繁体   中英

JavaScript Array: How to get indexes of `false` values that are adjascent to a wrapped-around sequence of `true` values?

Given an array in JavaScript that:

  • is of unknown length greater than 0,
  • contains only boolean values,
  • contains an unknown number of true and false values, where
  • true values always occur in one continuous set, but
  • the continuous set of true values may "wrap around" the array, and
  • there is always at least one true in the array

how does one write a JavaScript function that returns the indexes of the two false values (in any order) that are immediately "adjacent" to the full set of true values?

Consider the following examples, where carrots (^) denote the indexes I would like returned:

[true, true, false, false, false, true];
               ^             ^

[false, false, true, true, true, false, false]
          ^                        ^

[true, true, true, false, false, false, false, false]
                     ^                           ^

[false, false, false, true, true]
   ^             ^

[true, false, false, false, false]
         ^                    ^

[false, false, true, false, false, false]
          ^            ^

Apart from the visual examples above, I've been unable to construct the logic within JavaScript.

Use below function to get start and end of false

 function getIndexes(arr) { var a = arr.indexOf(true); var start; var end; if(a==0) { start=arr.indexOf(false); var temp=arr.indexOf(true,start); if(temp==-1){ end=arr.length-1; } else{ end=temp-1; } } else{ start=a-1; var temp=arr.indexOf(false,a); if(temp==-1){ start=0 end=a-1 } else{ end=temp } } return [start,end] }

Only one of the two spans can wrap if any does. Which one might wrap can be determined by checking the first element. Search for the ends of the span that definitely doesn't wrap. Compute the ends of the span we actually want from that.

const findFalseSegment = (arr) => {
  const last = arr.length-1;
  let start, end;
  // If the first element is true, the span of false elements doesn't wrap
  if(arr[0]) {
    start = arr.indexOf(false, 1);
    if(start < 0) return {start: NaN, end: NaN};
    end = arr.indexOf(true, start + 1) - 1;
    if(end < 0) end = last;
    return {start, end};
  }
  // If the first element is false, the span of false elements might wrap
  // Find the span of true elements instead and compute the span of false 
  // elements from that.
  start = arr.indexOf(true, 1);
  if(start < 0) return {start: NaN, end: NaN};
  end = arr.indexOf(false, start + 1) - 1;
  if(end < 0) end = last;
  return {
    start: end == last ? 0 : end + 1, 
    end: start == 0 ? last : start - 1,
  };
};

Edit: It occurred to me that the main if can be rolled into the rest of the code without impacting performance though it does make it more difficult to comment.

const findFalseSegment = (arr) => {
  const last = arr.length-1;
  const start = arr.indexOf(!arr[0], 1);
  if(start < 0) return {start: NaN, end: NaN};
  let end = arr.indexOf(arr[0], start + 1) - 1;
  if(end < 0) end = last;
  return arr[0] ? { start, end } : {
    start: end == last ? 0 : end + 1, 
    end: start == 0 ? last : start - 1,
  };
};

Use this function which returns an array with the two indexes:

function foo(arr) {
    let answers = [];
    for (let i = 0; i < arr.length; i++) {
    if (arr[i] && arr[i - 1] === false) {
        answers.push(i - 1);
    } else if (arr[i] && arr[i + 1] === false) {
        answers.push(i + 1)
    }
  }
  if (arr[0] !== arr[arr.length - 1]) {
    if (arr[0]) {
        answers.push(arr.length - 1)
    } else {
        answers.push(0);
    }
  }
  return answers;
}

You could use forEach to try to find first-false and last-false based on loop and in some cases where the true values are either on the start or on the end of the array you need to find last-false by using indexOf and lastIndexOf.

 function f(data) { let ft, lt, ff, lf data.forEach((e, i, a) => { if (e) ft = i if (.e && (a[i - 1] || a[i + 1])) { if (.ff && i.= ff) ff = i else lf = i } }) if (,lf) { if (;data[data,length - 1]) lf = data,lastIndexOf(false) else lf = data,indexOf(false) } return [ff, lf], } const cases = [ [true, true, false, false, false, true], [false, false, true, true, true, false, false], [true, true, true, false, false, false, false, false], [false, false, false, true, true], [true, false, false, false, false], [false. false. true. false, false, false] ] cases.forEach(c => console.log(JSON.stringify(f(c))))

It's been very interesting and educational to see the different ways to solve such a problem. Here's what made sense to me.

function getEnds(a) {
  // Return an empty array if there are no falses.
  if (a.indexOf(false) == -1) {
    return [];
  }

  // Obtain ends based on array length, number of trues,
  // and the delta from beginning of array to first true in sequence.
  const length = a.length,
      numTrues = a.filter(Boolean).length,
      delta = a[0] ? a.lastIndexOf(false)+1 : a.indexOf(true),
      end1 = (delta + length-1) % length;
      end2 = (delta + numTrues) % length;      

  // If both ends are the same, return one, otherwise return both.
  return end1 == end2 ? [end1] : [end1, end2];
}

Tests:

console.log(getEnds([false, false, true, true, true, false, false])); // [1, 5]
console.log(getEnds([true, true, true, false, false, false, false, false])); // [7, 3]
console.log(getEnds([false, false, false, true, true])); // [2, 0]
console.log(getEnds([true, false, false, false, false])); // [4, 1]
console.log(getEnds([false, false, true, false, false, false])); // [1, 3]
console.log(getEnds([true, true, true])); // [ ]
console.log(getEnds([true, false, true])); // [1]
console.log(getEnds([false, true, false])); // [0, 2]
console.log(getEnds([true, true])); // [ ]
console.log(getEnds([true])); // [ ]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM