Search a positive gradient in a dynamic-sized 2d array - javascript

I need to search a positive & negative gradient in a 2d array for values, for example.
Positive gradient: bottom left > middle > top right
Negative gradient: top left > middle > bottom right
[
['x', 'x', 'o'],
[null, null, 'o'],
['x', 'x', null]
]
I have 2 functions. First the value is found in a for loop, when found both of these functions run to search the positive & negative gradient of the maze and return true if a winning combination is found.
scanForNegativeGradientCombinationsworks, scanForPositiveGradientCombinations does not and ends in a typeError. I think the problem may be when reducing the rowIndex, I am pushing the game out of bounds, but im not sure.
const isDiagonalWinner = (rowCellValues, winCountCondition, player) => {
for(let rowIndex = 0; rowIndex < rowCellValues.length; rowIndex++){
for(let columnIndex = 0; columnIndex < rowCellValues[rowIndex].length; columnIndex++){
const cellValue = rowCellValues[rowIndex][columnIndex];
if(cellValue === player.symbol) {
console.log('initiating player scan for ', player.symbol, 'at', [rowIndex, columnIndex]);
if(scanForNegativeGradientCombinations(columnIndex, rowIndex, rowCellValues, player.symbol, winCountCondition)) {
return true
}
if(scanForPositiveGradientCombinations(columnIndex, rowIndex, rowCellValues, player.symbol, winCountCondition)) {
return true
}
}
}
}
return null;
};
Above is the function that will call the following 2 functions.
const scanForNegativeGradientCombinations= (columnIndex, rowIndex, rowCellValues, playerSymbol, winCountCondition) => {
let counter = 0;
while(rowIndex < rowCellValues.length && columnIndex < rowCellValues[rowIndex].length){
if(rowCellValues[rowIndex][columnIndex] === playerSymbol){
counter++;
}
if(counter >= winCountCondition){
return true;
}
rowIndex++;
columnIndex++;
}
return false;
}
const scanForPositiveGradientCombinations= (columnIndex, rowIndex, rowCellValues, playerSymbol, winCountCondition) => {
let counter = 0;
while(rowIndex < rowCellValues.length && columnIndex < rowCellValues[rowIndex].length){
if(rowCellValues[rowIndex][columnIndex] === playerSymbol){
counter++;
}
if(counter >= winCountCondition){
return true;
}
rowIndex--;
columnIndex++;
}
return false;
}

function sequence(n) { return Array(n).fill().map((_,i)=>i) }
function diagonalWin(board) {
let d = board.length
let seq = sequence(d)
return board[0][0]!==null && seq.every(i=>board[i][i]===board[0][0]) ||
board[0][d-1]!==null && seq.every(i=>board[i][d-i-1]===board[0][d-1])
}
console.log(diagonalWin([
['x', 'x', 'o'],
[null, null, 'o'],
['x', 'x', null]
]))
console.log(diagonalWin([
['x', 'x', 'o'],
[null, 'x', 'o'],
['x', 'x', 'x']
]))
console.log(diagonalWin([
['x', 'x', 'o'],
[null, 'o', 'o'],
['o', 'x', 'x']
]))

Related

Check if items in 2d array form a rectangle

I am looking for a simple JavaScript formula that will calculate whether the X that the user types in a box forms either a rectangle or a square.
I have attempted a loop to do this but I think I have made this way too complex.
Basically I have stored the data like this (typescript)
public proposedArray: Array<Array<boolean>> = [];
I have sketched a diagram below in what would be valid/invalid options. Can anyone please help me?
Thanks!
If you take in the matrix as a multi-line string, like:
ooooo
ooxxo
ooxxo
ooooo
...then you can use this regular expression for making the validation:
^(o*\n)*(o*x+)o*\n(\2o*\n)*[o\n]*$
Trailing o are ignored, so the lines do not have to have the same length to still detect a rectangle.
Here is a snippet where the input is taken from a <textarea> element. Edit the text to see the result:
const isRectangle = (grid) =>
/^(o*\n)*(o*x+)o*\n(\2o*\n)*[o\n]*$/.test(grid + "\n");
// I/O handling
const input = document.querySelector("textarea");
input.addEventListener("input", refresh);
function refresh() {
const text = input.value.toLowerCase();
document.querySelector("span").textContent = isRectangle(text);
}
refresh();
<textarea rows=10>
ooooo
ooxoo
ooxxo
ooooo
</textarea><br>
Is rectangle: <span></span>
If you already have the 2-dimensional matrix, then you can of course first transform that matrix to such a multiline string and then perform the regex-test.
Yes loops sounds the naive option. Looping until found a "true". Then caculate width and height, and expect all cells withing range to be "true" as well. If that is so, then expect no more "trues" having cleared the ones we found.
var mat = [
[0, 0, 0],
[1, 1, 0],
[1, 1, 0]
];
function has_square(mat) {
var found_square = false;
for (var i = 0; i < mat.length; i++) {
for (var j = 0; j < mat[i].length; j++) {
var value = mat[i][j]
if (value) {
if (found_square) {
// not allowed 2 squares
return false;
}
var w = 1;
for (var k = j + 1; k < mat[i].length; k++) {
if (!mat[i][k]) {
break;
}
w++;
}
var h = 1;
for (var l = i + 1; l < mat.length; l++) {
if (!mat[l][j]) {
break;
}
h++;
}
// now expect all to be true in [i,j] - [i+h, j+w]
for (var y = 0; y < h; y++) {
for (var x = 0; x < w; x++) {
if (!mat[i + y][j + x]) {
return false;
}
// clear values
mat[i + y][j + x] = 0
}
}
found_square = true;
}
}
}
return found_square;
}
console.log(has_square(mat))
With a simple clamp helper that constrains a value to be between minimum and maximum values, you can simply calculate the minimum and maximum X- and Y-indices, and then use them to test whether every cell within those bounds has the value "x", with something like this:
const clamp = (min, max, x) => Math .max (min, Math .min (max, x))
const xRect = (vss) => {
const minX = clamp (0, vss [0] .length, Math .min (...vss .map (vs => vs .indexOf ('x')) .filter (v => v > -1)))
const minY = clamp (0, vss .length, vss .findIndex (v => v .includes ('x')))
const maxX = clamp (0, vss [0] .length, Math .max (...vss .map (vs => vs .lastIndexOf ('x')) .filter (v => v > -1)))
const maxY = clamp (0, vss .length, vss .length - [...vss] .reverse() .findIndex (v => v .includes ('x')) - 1)
return vss .slice (minY, maxY + 1) .every (
vs => vs .slice (minX, maxX + 1) .every (v => v == 'x')
)
}
console .log (xRect ([
['o', 'o', 'o'],
['x', 'x', 'o'],
['x', 'o', 'x']
]))
console .log (xRect ([
['o', 'o', 'o'],
['x', 'x', 'o'],
['o', 'x', 'o']
]))
console .log (xRect ([
['o', 'o', 'o'],
['x', 'x', 'o'],
['x', 'x', 'o']
]))
console .log (xRect ([
['o', 'o', 'o'],
['x', 'o', 'o'],
['x', 'o', 'o']
]))
console .log (xRect ([
['o', 'o', 'o'],
['x', 'o', 'o'],
['o', 'o', 'o']
]))
console .log (xRect ([
['o', 'o', 'o'],
['x', 'o', 'x'],
['o', 'o', 'o']
]))
The calculations of those values is tedious but not difficult. Then we simply check if every value in every row of the subarray indicated by those minima and maxima has value "x".
An idea that popped my mind was to create an algorithm that:
Consumes inwards, or top-to-bottom, left-to-right (from all 4 sides recursively) all the "O" edges. A valid edge to consume must be made exclusively of "O" values.
Once all of the four "edge consumers" are done (no more iterations are possible on all four sides) check if the remaining 2D array consists exclusively of "X" values.
Visually:
Example:
Here I use transpose, but you can create a trimHorizontal function if you want
const trim = a => {
const t = a.findIndex(a=>a.some(x=>x));
return t < 0 ? a : a.slice(t, a.findLastIndex(a=>a.some(x=>x))+1);
};
const transpose = a => a[0].map((_,i)=>a.map(r=>r[i]));
const hasRect = a => !trim(transpose(trim(a))).flat().some(v=>!v);
console.log(hasRect([
[0, 0, 0],
[0, 0, 1],
])); // true
console.log(hasRect([
[0, 0, 0],
[0, 1, 1],
[0, 1, 1],
])); // true
console.log(hasRect([
[1, 0, 0],
[0, 0, 1],
])); // false
console.log(hasRect([
[0, 1, 0],
[0, 0, 0],
[0, 1, 0],
])); // false

checking pair value in array

here i wanna ask about how to check the data in the array if not same value/data on next index, push it on new array,
here is the example:
function check(arr){
let text = "";
let newArr = [];
for(let i = 0 ; i < arr.length-1 ; i++){
if(arr[i] !== arr[i+1]){
text = arr[i] + ' and ' + arr[i+1];
newArr.push(text)
}
}
return newArr
};
console.log(check([ 'A', 'A', 'M', 'Y', 'I', 'W', 'W', 'M', 'R', 'Y' ]))
// output "A and M", "A and Y", "I and W", "W and M", "R and Y"]
console.log(check([ 'a', 'b', 'j', 'j', 'i', 't' ]))
my result here is not i want, it was repeated the data which i already push . in newArr
i want the ouput like this :
["A and M", "A and Y", "I and W", "W and M", "R and Y"]
because of each array not the same initial,
i hope this question makes sense
You can do the following :
function check(arr) {
let text = "";
let newArr = [];
for (let i = 0, next = 0; i < arr.length; i++) {
if (next == 2) {
next = 0;
i += 2;
}
if (arr[i + 1] !== undefined) {
if (arr[i + 2] !== undefined) {
text = arr[i] + ' and ' + arr[i + 2];
} else {
text = arr[i] + ' and ' + arr[i + 1];
}
newArr.push(text)
}
next += 1;
}
return newArr
};
console.log(check(['A', 'A', 'M', 'Y', 'I', 'W', 'W', 'M', 'R', 'Y']))
console.log(check(['a', 'b', 'j', 'j', 'i', 't']))

finding sequential values in js array

What's wrong with this code?
I'm experimenting with a simple card game to see if one has a straight.
The logic is to just check if the next value in the array is the current value + 1
let arr = [
[ 'd', 13 ],
[ 'f', 12 ],
[ 'z', 11 ],
[ 'd', 10 ],
[ 'd', 9 ]
];
arr = arr.sort((a,b) => a[1] - b[1]);
const isSeq = arr => {
for (let i = 0; i < arr.length - 1; i++) {
console.log(arr[i][1]+1, arr[i+1][1])
if (arr[i][1]+1 !== arr[i+1][1]) {
return false;
} else {
return true;
}
}
}
isSeq(arr);
You need to remove the else part, because this would exit the function even if true in the first iteration.
let arr = [[ 'd', 13 ], [ 'f', 12 ], [ 'z', 11 ], [ 'd', 10 ], [ 'd', 9 ]];
arr = arr.sort((a, b) => a[1] - b[1]);
const isSeq = arr => {
for (let i = 0; i < arr.length - 1; i++) {
console.log(arr[i][1] + 1, arr[i + 1][1]);
if (arr[i][1] + 1 !== arr[i + 1][1]) {
return false;
}
}
return true;
};
console.log(isSeq(arr));
You're quick returning after the first check, one way or the other.
if (arr[i][1]+1 !== arr[i+1][1]) {
return false;
} else {
return true;
}
You should return after all the checks have been done:
for (let i = 0; i < arr.length - 1; i++) {
console.log(arr[i][1]+1, arr[i+1][1])
if (arr[i][1]+1 !== arr[i+1][1]) {
return false;
}
}
return true;
You can also do this with Array#every():
let arr = [
['d', 13],
['f', 12],
['z', 11],
['d', 10],
['d', 9]
];
arr = arr.sort((a, b) => a[1] - b[1]);
function isSeq(data) {
return data.every((num, i) => (i === data.length - 1) || (num[1] === (data[i + 1][1] - 1)));
}
console.log(isSeq(arr));
This first checks if the index is the last, and if not makes sure that the current element is equal to array[i+1]-1
Instead if else with return, you can use break:
Take the first value as the current and iterate the remainder of the input. Compare each entry to the current. If the sequence is broken, break from the loop. Before moving to the next entry, set this as one as the new current.
let arr = [
[ 'd', 13 ],
[ 'f', 12 ],
[ 'z', 11 ],
[ 'd', 10 ],
[ 'd', 9 ]
];
arr = arr.sort((a,b) => a[1] - b[1]);
const isSeq = arr => {
let isStraight = true
let current = arr[0][1]
for (let i = 1; i < arr.length; i++) {
if (arr[i][1] !== current + 1) {
isStraight = false
break
}
current = arr[i][1]
}
return isStraight
}
console.log(isSeq(arr))

Finding every second element in a repeating pattern

Data with repeated 'i's followed by 'i's and/or 't's.
data = ['i','t','t','i','i','t','t','t']
Trying to retrieve the index of the last 't' in the pattern ['i','t','t']:
[2,6] // ['i','t','t','i','i','t','t','t'] # position of the returned 't's
// _______ ^ _______ ^
I'm looking for a non-recursive solution using (pure) functions only, using ramdajs for example.
Tried to use reduce and transduce, but unsuccessful sofar.
One approach would be to use R.aperture to iterate over a 3-element sliding window of the data list, then tracking the position of any sub-list that equals the pattern ['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>
A point-free version of this approach could look something like the following, though whether this is preferable comes down to a preference of style/readability.
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>
You could use a nested approach with a temporary array for checking the same pattern for different starting points. This proposal works with an arbitrary length of pattern and returns the index of the predefined pattern.
This solution features obviously plain 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; }
You can do it using Array.prototype.reduce() with a simple condition.
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']
You can do simply by for loop and check last values of array:
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)
data.filter((c, i, d) => c === 't' && d[i - 1] === 't' && d[i - 2] === 'I')
**No negative indexes: **
const matchMaker = () => {
let memo = [‘a’, ‘b’];
return (c, i, d) => {
memo.unshift(c);
return memo[1] + memo[2] + c === 'itt';
}
};
data.filter(matchMaker());
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']));
.as-console-wrapper { max-height: 100% !important; top: 0; }
In case anyone wants to see an example without using a library that does not use look ahead or behinds ("i + 1" or "i - 2'", etc.).
I think it works similarly to what the Ramda approach does, but I chose to combine the partitioning and equality check in the same loop:
For every step in reduce
Take a section of the array matching the pattern length
Check if it is equal to the pattern
If it is, add the index of the last element in the section to the result of reduce
The code, in which pattern and data are both arrays of strings:
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]);
I tested on several data sets and think it meets all requirements:
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(", ")
)
)
Two years later, lost traveler stumbles upon this question and notices that for variable sized (and especially large pattern with even larger input array or multiple input arrays), classical KMP algorithm would be great.
I think it is worth studing this algorithm.
We will start with simple imperative implementation. Then switch to (at least for me) more intuitive (but probably slightly less optimal, and definitely less optimal when it comes to memory) version with finite automaton. At the end, we'll see something that looks like functional but it is not 100% pure. I wasn't in a mood to torture my self with pure functional implementation of KMP in JS :).
Prefix function KMP, imperative implementation:
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; }
Finate automaton KMP implementation:
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; }
Funcational-ish KMP implementation:
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; }

Fetching JavaScript array elements after consecutive occurrence of an element

I have a JavaScript array like:
var myArray = ['a', 'x', 'b', 'x', 'x', 'p', 'y', 'x', 'x', 'b', 'x', 'x'];
I want to fetch only those elements of the array that come after 2 consequent occurrences of a particular element.
i.e. in the above array, I want to fetch all the elements that come after consequent 'x', 'x'
So my output should be:
'p'
'b'
I have a solution like :
var arrLength = myArray.length;
for (var i = 0; i < arrLength; i++) {
if(i+2 < arrLength && myArray[i] == 'x' && myArray[i+1] == 'x') {
console.log(myArray[i+2]);
}
};
This satisfies my needs, but it is not so generic.
For eg. if I have to check for 3 consequent occurrences, then again I have to add a condition inside if for myArray[i+2] == 'x' and so on.
Could anyone provide a better way to fetch the elements?
The functional way would be to use recursion. With an ES6 spread, you can pretty much emulate the terseness of a truly 'functional' language :-)
var myArray = ['a', 'x', 'b', 'x', 'x', 'p', 'y', 'x', 'x', 'b', 'x', 'x'];
function reducer(acc, xs) {
if (xs.length > 2) {
if (xs[0] === xs[1]) {
// add the third element to accumulator
// remove first three elements from xs
// return reducer([xs[2], ...acc], xs.slice(3));
// or per Nina's question below
return reducer([xs[2], ...acc], xs.slice(1));
} else {
// remove first element from xs and recurse
return reducer(acc, xs.slice(1))
}
} else {
return acc;
}
}
console.log(reducer([], myArray));
A generic straight forward approach for any comparable content.
function getParts(array, pattern) {
return array.reduce(function (r, a, i) {
i >= pattern.length && pattern.every(function (b, j) {
return b === array[i + j - pattern.length];
}) && r.push(a);
return r;
}, []);
}
function p(o) {
document.write('<pre>' + JSON.stringify(o, 0, 4) + '</pre>');
}
p(getParts(['a', 'x', 'x', 'x', 'x', 'p', 'y', 'x', 'x', 'b', 'x', 'x'], ['x', 'x']));
p(getParts(['a', 'x', 'b', 'x', 'x', 'p', 'y', 'x', 'x', 'b', 'x', 'x'], ['a', 'x', 'b']));
p(getParts(['a', 'b', 'c', 'd', 'z', 'y', 'a', 'b', 'c', 'd', 'x', 'x'], ['a', 'b', 'c', 'd']));
p(getParts([41, 23, 3, 7, 8, 11, 56, 33, 7, 8, 11, 2, 5], [7, 8, 11]));
You can try following logic
var myArray = ['a', 'x', 'b', 'x', 'x', 'p', 'y', 'x', 'x', 'b', 'x', 'x'];
function search(ch, times) {
var splitStr = "";
for(var i = 0; i < times; i++) {
splitStr += ch;
} // Generate the split string xx in the above case.
var str = myArray.join(''); // Join array items into a string
var array = str.split(splitStr); // Split the string based on split string
var result = {};
// iterate on the array starting from index 1 as at index 0 will be string before split str
for (var i = 1 ; i < array.length; i++) {
if(array[i] !== "") {
result[array[i].substring(0,1)] = ''; // A map in order to avoid duplicate values
}
}
return Object.keys(result); // return the keys
}
console.dir(search('x',2));
Here is a straightforward iterative solution. We maintain an array consecutive of consecutive elements. If that array gets to length 2, then the next element is printed and consecutive is reset.
var arr = ['a', 'x', 'b', 'x', 'x', 'p', 'y', 'x', 'x', 'b', 'x', 'x'];
var REPEATS_NEEDED = 2;
var consecutive = [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (consecutive.length === REPEATS_NEEDED) {
console.log(arr[i]);
consecutive = [arr[i]];
continue;
}
// either add to or reset 'consecutive'
if (arr[i] === consecutive[0]) {
consecutive.push(arr[i]);
} else {
consecutive = [arr[i]];
}
};
You can create an additional function isItGood like this:
var myArray = ['a', 'x', 'b', 'x', 'x', 'p', 'y', 'x', 'x', 'b', 'x', 'x'];
var arrLength = myArray.length;
for (var i = 0; i < arrLength; i++) {
isItGood(myArray, i, 'x', 2);
};
function isItGood(arr, i, elem, total) {
for ( var j = 0 ; j < total ; j++ ) {
if ( i + total >= arr.length || arr[i+j] != elem ) {
return;
}
}
console.log(arr[i+total]);
// just to see the result (no need to open a console)
document.getElementById('p').innerHTML+=("<br/>"+arr[i+total]);
}
<p id="p">Result: </p>
If I had to write this in Scala instead of JavaScript I could just do it in one line.
myArray.sliding(3).filter(l => l(0) == 'x' && l(1) == 'x').map(l => l(2))
So I guess I could do it the same way in JS if I implement the sliding function myself.
e.g.
function sliding(array, n, step) {
if(!step) step = 1;
var r = [];
for(var i = 0; i < array.length - n + 1; i += step) {
r.push(array.slice(i, i + n));
}
return r;
}
var result = sliding(myArray, 3).filter(l => l[0] === "x" && l[1] === "x").map(l => l[2]);
The only downside here is that this runs slower than a more iterative approach. But that only matters for very big arrays.
Try using for loop using variables referencing previous index, current index, next index of array
var myArray = ["a", "x", "b", "x", "x", "p", "y", "x", "x", "b", "x", "x"];
for (var res = [], curr = 0, prev = curr - 1, match = curr + 1
; curr < myArray.length - 1; curr++, prev++, match++) {
if (myArray[curr] === myArray[prev]) res.push(myArray[match]);
};
console.log(res);
document.body.textContent = res;

Categories