在重复模式中查找每个第二个元素

时间:2017-06-17 07:30:58

标签: javascript functional-programming ramda.js

重复'i'后跟'i'和/或't'的数据。

data = ['i','t','t','i','i','t','t','t']

尝试检索模式['i','t','t']中最后一个't'的索引:

[2,6] // ['i','t','t','i','i','t','t','t'] # position of the returned 't's
      //   _______ ^       _______ ^

我正在寻找仅使用(纯)函数的非递归解决方案,例如使用ramdajs。

尝试使用reducetransduce,但未成功使用。

8 个答案:

答案 0 :(得分:2)

您可以使用带有临时数组的嵌套方法来检查不同起点的相同模式。此提议使用任意长度的模式,并返回预定义模式的索引。

此解决方案显然具有简单的Javascript。

index   i  t  i  t  t  i  i  t  t  t   temp    result    comment
-----  ------------------------------  ------  --------  ------------
    0  <i>                             [0]     []        match
    1   i <t>                          [0]     []        match
          <->                          [0]     []        no match
    2   i  t <->                       []      []        no match
             <i>                       [2]     []        match
    3         i <t>                    [2]     []        match
                <->                    [2]     []        no match
    4         i  t <t>                 []      [4]       pattern found
                   <->                 []      [4]       no match
    5                 <i>              [5]     [4]       match
    6                  i <->           []      [4]       no match
                         <i>           [6]     [4]       match
    7                     i <t>        [6]     [4]       match
                            <->        [6]     [4]       no match
    8                     i  t <t>     []      [4, 8]    pattern found
                               <->     []      [4, 8]    no match
    9                             <->  []      [4, 8]    no match

<t> matches 't' at position
<-> does not match at position

function getPatternPos(array, pattern) {
    var result = [];

    array.reduce(function (r, a, i) {
        return r.concat(i).filter(function (j) {
            if (i - j === pattern.length - 1 && a === pattern[i - j]) {
                result.push(i);
                return false;
            }
            return a === pattern[i - j];
        });
    }, []);
    return result;
}

console.log(getPatternPos(['i', 't', 't', 'i', 'i', 't', 't', 't'], ['i', 't', 't']));
// [2, 6]

console.log(getPatternPos(['i','t','i', 't', 't', 'i', 'i', 't', 't', 't'], ['i', 't', 't']));
// [4, 8]

console.log(getPatternPos(['a', 'b', 'a', 'b', 'b', 'a', 'b', 'c', 'd'], ['a', 'b', 'c']));
// [7]
.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 1 :(得分:2)

一种方法是使用R.aperture迭代data列表的3元素滑动窗口,然后跟踪任何等于模式['i', 't', 't']的子列表的位置

const data = ['i','t','t','i','i','t','t','t']

const isPattern = R.equals(['i', 't', 't'])

const reduceWithIdx = R.addIndex(R.reduce)

const positions = reduceWithIdx((idxs, next, idx) =>
  isPattern(next) ? R.append(idx + 2, idxs) : idxs
, [], R.aperture(3, data))

console.log(positions)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.24.1/ramda.min.js"></script>

这种方法的无点版本可能看起来像下面这样,但这是否更可取于风格/可读性的偏好。

const data = ['i','t','t','i','i','t','t','t']

const isPattern = R.equals(['i', 't', 't'])

const run = R.pipe(
  // create sliding window of 3 elements
  R.aperture(3),

  // zip sliding window with index
  R.chain(R.zip, R.compose(R.range(0), R.length)),

  // filter matching pattern
  R.filter(R.compose(isPattern, R.nth(1))),

  // extract index
  R.map(R.compose(R.add(2), R.head))
)

console.log(run(data))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.24.1/ramda.min.js"></script>

答案 2 :(得分:0)

您只需通过for循环并检查数组的最后一个值:

        var data = ['i','t','t','i','i','t','t','t'];
        var positions = new Array();

        for(var i=2; i< data.length; i++){
          if(data[i-2] === 'i' && data[i-1] === 't' && data[i] === 't') {
            positions.push(i)
          }
        }
        
        console.log(positions)

答案 3 :(得分:0)

您可以使用Array.prototype.reduce()使用简单条件来执行此操作。

&#13;
&#13;
data = ['i','t','t','i','i','t','t','t']


var newData = data.reduce(function (acc, item, index) {
    // Check if current element is `t` and the item before it is `i`, `t` 
    if (item === 't' && data[index - 1] === 'i' && data[index - 2] === 't') {
        acc.push(item)
    }
    return acc;
}, []);

console.log(newData); // ['t', 't']
&#13;
&#13;
&#13;

答案 4 :(得分:0)

string r = "s" + number;

**没有负面指数:**

data.filter((c, i, d) => c === 't' && d[i - 1] === 't' && d[i - 2] === 'I')

答案 5 :(得分:0)

&#13;
&#13;
function getPattern(arr, p) {
  var r = [],
    dir = [];
  for (let [i, v] of arr.entries()) {
    dir = dir.concat(i).filter(function(x) {
      if (v === p[i - x] && i - x === p.length - 1) {
        r.push(i);
        return false;
      }
      return v === p[i - x];
    })
  };
  return r;
}

console.log(getPattern(['i', 't', 't', 'i', 'i', 't', 't', 't'], ['i', 't', 't']));
console.log(getPattern(['i', 't', 'i', 't', 't', 'i', 'i', 't', 't', 't'], ['i', 't', 't']));
console.log(getPattern(['a', 'b', 'a', 'b', 'b', 'a', 'b', 'c', 'd'], ['a', 'b', 'c']));
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
&#13;
&#13;

答案 6 :(得分:0)

如果有人想要查看示例而不使用不使用前方或后方的库(“i + 1”或“i - 2'”等)。

我认为它与Ramda方法的工作方式类似,但我选择在同一个循环中组合分区和相等检查:

  • reduce
  • 中的每一步
  • 获取与pattern长度
  • 匹配的数组部分
  • 检查它是否等于模式
  • 如果是,请将部分中最后一个元素的索引添加到reduce
  • 的结果中

代码,其中patterndata都是字符串数组:

const findPattern = (pattern, data) => data.reduce(
  (results, _, i, all) => 
    // Check if a slice from this index equals the pattern
    arrEqual(all.slice(i, i + pattern.length), pattern)
      // Add the last index of the pattern to our results
      ? (results.push(i + pattern.length - 1), results)
      // or, return what we had
      : results, 
    []);

// Utility method to check array equality
const arrEqual = (arr1, arr2) =>
  arr1.length === arr2.length &&
  arr1.every((x, i) => x === arr2[i]);

我测试了几个数据集并认为它符合所有要求:

const findPattern = (pattern, data) => data.reduce(
  (results, _, i, all) => 
    arrEqual(all.slice(i, i + pattern.length), pattern)
      ? push(results, i + pattern.length - 1)
      : results, 
    []);


const arrEqual = (arr1, arr2) =>
  arr1.length === arr2.length &&
  arr1.every((x, i) => x === arr2[i]);
  
const push = (xs, x) => (xs.push(x), xs);

// For just string patterns we could also do:
// const arrEqual = (arr1, arr2) => arr1.join("") === arr2.join("");

// Test cases
const dataSets = [
  // (i) marks a matching index
  // [i] marks a last matching index that should be returned
  //  |  marks a new start

  { pattern: ["i","t","t"], input: ['i','t','t','i','i','t','t','t'],         output: [2, 6] },
  //                               |(0) (1) [2]| 3 -(4) (5) [6]| 7     
  
  { pattern: ["i","t"],     input: ['i','t','i','t','t','i','i','t','t','t'], output: [1, 3, 7] },
  //                               |(0) [1]|(2) [3]| 4 | 5 |(6) [7]| 8 | 9
  
  { pattern: ["i","t","t"], input: ['i','t','i','t','t','i','i','t','t','t'], output: [4, 8] },
  //                               |(0) (1)|(2) (3) [4]| 5 |(6) (7) [8]| 9
  
  { pattern: ["i","t","i"], input: ['i','t','i','t','i','t','i','t','i','t'], output: [2, 4, 6, 8] }
  //                               |(0) (1) [2]|           |(6) (7) [8]| 9
  //                                       |(2) (3) [4]  
  //                                               |(4) (5) [6]  
];

dataSets.forEach(({pattern, input, output}) => 
  console.log(
    "| input:", input.join(" "), 
    "| control:", output.join(", "), 
    "| answer:", findPattern(pattern, input).join(", ")
  )
)

答案 7 :(得分:0)

两年后,迷路的旅行者迷迷糊糊地问了这个问题,并注意到对于可变大小(尤其是具有更大输入数组或多个输入数组的大型模式),经典KMP algorithm会很棒。

我认为值得研究这种算法。

我们将从简单的命令式实现开始。然后使用有限自动机切换到(至少对我来说)更直观(但可能在最佳化方面稍差一些,而在内存方面肯定不是最佳化)版本。最后,我们将看到一些看起来像功能的东西,但它不是100%纯的。我不想用JS中的KMP的纯功能实现来折磨自己:)。

前缀功能KMP,命令性实现:

function getPatternPos(array, pattern) {
    const result = [];

	// trying to explain this is a waste of time :)

    function createPrefix(pattern) {
    	// initialize array with zeros
    	const prefix = Array.apply(null, Array(pattern.length)).map(Number.prototype.valueOf, 0);
    	let s = 0;
    	prefix[0] = 0;

    	for (let i = 1; i < pattern.length; ++i) {
			while (s > 0 && pattern[s] !== pattern[i]) {
				s = prefix[s - 1];
			}
			if (pattern[i] === pattern[s]) {
    				++s;
			}
			prefix[i] = s;
    	}

    	return prefix;
    }

    const prefix = createPrefix(pattern);
    let s = 0;
    for (let i = 0; i < array.length; ++i) {
		while (s > 0 && pattern[s] !== array[i]) {
			s = prefix[s - 1];
		}
		if (array[i] === pattern[s]) {
			++s;
		}
		if (s === pattern.length) {
			result.push(i);
			s = 0;
		}
    }

    return result;
}

console.log(getPatternPos(['i', 't', 't', 'i', 'i', 't', 't', 't'], ['i', 't', 't']));
// [2, 6]
console.log(getPatternPos(['i','t','i', 't', 't', 'i', 'i', 't', 't', 't'], ['i', 't', 't']));
// [4, 8]
console.log(getPatternPos(['a', 'b', 'a', 'b', 'b', 'a', 'b', 'c', 'd'], ['a', 'b', 'c']));
// [7]
console.log(getPatternPos("ababxabababcxxababc".split(""), "ababc".split("")));
// [11, 18]
console.log(getPatternPos("abababcx".split(""), "ababc".split("")));
.as-console-wrapper { max-height: 100% !important; top: 0; }

自动机KMP的最终实现:

function getPatternPos(array, pattern) {
    const result = [];

    function patternCode(i) {
    	return pattern[i].charCodeAt(0);
    }

    function createStateMachine(pattern) {
		// return single dimensional array as matrix instead of array of arrays,
		// for better perfomanse (locality - cache optimizations) and memory usage.
    	// zero initialize matrix
    	const sm = Array.apply(null, Array(256 * pattern.length)).map(Number.prototype.valueOf, 0);
    	let s = 0;
    	sm[patternCode(0) * pattern.length + 0] = 1;

    	for (let i = 1; i < pattern.length; ++i) {
			// go to same states as if we would go after backing up, so copy all
    		for (let code = 0; code < 256; ++code)
				sm[code * pattern.length + i] = sm[code * pattern.length + s];

			// only in case of current symbol go to different/next state
			sm[patternCode(i) * pattern.length + i] = i + 1;

			// update the state that fallows backup path
    		s = sm[patternCode(i) * pattern.length + s];
    	}

    	return sm;
    }

    const sm = createStateMachine(pattern);
    numStates = pattern.length;
	let s = 0;
	// now simply fallow state machine
    for (let i = 0; i < array.length; ++i) {
    	s = sm[array[i].charCodeAt(0) * numStates + s];
    	if (s === pattern.length) {
    		result.push(i);
    		s = 0;
    	}
    }

    return result;
}

console.log(getPatternPos(['i', 't', 't', 'i', 'i', 't', 't', 't'], ['i', 't', 't']));
// [2, 6]
console.log(getPatternPos(['i','t','i', 't', 't', 'i', 'i', 't', 't', 't'], ['i', 't', 't']));
// [4, 8]
console.log(getPatternPos(['a', 'b', 'a', 'b', 'b', 'a', 'b', 'c', 'd'], ['a', 'b', 'c']));
// [7]
console.log(getPatternPos("ababxabababcxxababc".split(""), "ababc".split("")));
// [11, 18]
console.log(getPatternPos("abababcx".split(""), "ababc".split("")));
.as-console-wrapper { max-height: 100% !important; top: 0; }

功能性KMP实施:

function getPatternPos(array, pattern) {
	// pure function that creates state machine,
	// but it's implementation is not complitely pure internally.
	function createStateMachine(pattern) {
		const initState = Object.create(null);
		initState[pattern[0]] = Object.create(initState);

		const  {currState: finalState} = pattern.slice(1).reduce(function(acc, cval, cidx) {
			const newFallbackState = acc.fallbackState[cval] || initState;
			// WARNING: non-functional/immutable part,
			// to make it complitely pure we would probably need to
			// complicate our lives with better data structures or
			// lazy evalutaion.
			acc.currState[cval] = Object.create(newFallbackState);
			return {currState: acc.currState[cval], fallbackState: newFallbackState};
		}, {currState: initState[pattern[0]], fallbackState: initState});

		return {initState: initState, finalState: finalState};
	}

	const {initState, finalState} = createStateMachine(pattern);

	return array.reduce(function (acc, cval, cidx, array) {
		const newState = acc.currState[cval];
		if (typeof newState === 'undefined') {
			return {currState: initState, result: acc.result};
		}
		if (newState === finalState) {
			// WARNING: not purly functional/immutable,
			// still implemenations of JS pure functional/immutable libraries
			// probaly use mutation under the hood, and just make it look pure,
			// this is what happens here also :)
			acc.result.push(cidx);
			return {currState: initState, result: acc.result};
		}
		return {currState: newState, result: acc.result};
	}, {currState: initState, result: []}).result;
}

console.log(getPatternPos(['i', 't', 't', 'i', 'i', 't', 't', 't'], ['i', 't', 't']));
// [2, 6]
console.log(getPatternPos(['i','t','i', 't', 't', 'i', 'i', 't', 't', 't'], ['i', 't', 't']));
// [4, 8]
console.log(getPatternPos(['a', 'b', 'a', 'b', 'b', 'a', 'b', 'c', 'd'], ['a', 'b', 'c']));
// [7]
console.log(getPatternPos("ababxabababcxxababc".split(""), "ababc".split("")));
// [11, 18]
console.log(getPatternPos("abababcx".split(""), "ababc".split("")));
.as-console-wrapper { max-height: 100% !important; top: 0; }