JS查找数组中的所有序列

时间:2019-05-21 08:09:17

标签: javascript arrays

给出具有以下结构的对象数组(固定长度):

{type: 'A', value: 1}

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

找到所有类型为“ A”的对象序列并返回其索引的最简单方法是什么?

一个例子: 对于以下数组:

[
  {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'}
]

输出应为以下数组:

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

我猜天真的实现将是使用forEach进行迭代,并具有许多复杂的条件,但是有没有更简单的技术?

谢谢。

7 个答案:

答案 0 :(得分:3)

您可以减少数组并更改值和类型以创建新组。

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; }

答案 1 :(得分:2)

您可以像这样使用reduce。添加变量prev来跟踪先前的type是什么。如果当前的type是您要查找的类型:如果上一项具有不同的type,则添加一个对象。否则,只需增加length属性

let input = [{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 output = input.reduce((acc, { type, value }, i) => {
  if (type === 'A') {
    if (prev !== type) {
      acc.push({ startIndex: i, startValue: value, length: 1 })
    } else {
      acc[acc.length - 1].length++
    }
  }

  prev = type
  return acc;
}, [])

console.log(output)

答案 2 :(得分:0)

只需使用forEach循环。它易于阅读,并且不会使您的代码复杂化。

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'))

答案 3 :(得分:0)

我认为仅使用for循环遍历数组并构造所需的答案就不会更简单。此外,此解决方案具有线性复杂度,因为您只需遍历数组一次。不确定为什么需要“许多复杂条件”。

对我来说,这样的事情似乎还可以:

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') {
    // account for the first entry found
    if (result.length === 0) {
      result.push({startIndex: index, length: 1, startValue: el.value});
    } else {
      const lastSequence = result[result.length - 1];
      // check if the we are in a sequence
      if (lastSequence.startIndex + lastSequence.length === index) {
        lastSequence.length += 1;
      } else {
      // if we are not in a sequence - create a new one
        result.push({startIndex: index, length: 1, startValue: el.value});
      }
    }
  }
}

console.log(result);

答案 4 :(得分:0)

For your specified 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;
}, [])

答案 5 :(得分:0)

您可以在forEach中使用类似这样的声明性方法,而不是在命令中强制使用:

  const a = [
  {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 filtered = a.map((v,i) => {
return v.type == 'A' ? {startIndex: i,startValue: v.value} : null
}).filter(x => x).reduce((acc,v) => {

if (acc.filter(x => x.startIndex + x.length  == v.startIndex).length > 0){
acc[acc.length-1].length ++;
} else {
v.length = 1;
acc.push(v);
}
return acc;
},[]);
console.log(filtered);

答案 6 :(得分:0)

尽管这里已经有很多不错的答案,但我想我还是要添加一个,它解决了细分列表的更一般的情况。使用此代码,可以指定一个segmenter函数,该函数比较两个项目并确定是否应该开始一个新的段。

有了这些细分后,获得所需的最终答案非常简单。

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);