Related
When running my sudoku backtracking algorithm using random ordering for finding new available locations, it takes way longer than when finding the available locations, left to right, top to bottom. Why? How should I change the code to have quick random ordering?
//Taking forever
let posOrder = [[4, 4], [4, 0], [7, 0], [4, 8], [2, 3], [0, 8], [6, 0], [0, 6], [0, 5], [5, 4], [8, 2], [7, 7], [5, 1], [6, 3], [3, 2], [3, 3], [1, 2], [6, 2], [0, 7], [4, 2], [1, 4], [0, 1], [1, 1], [7, 1], [5, 2], [0, 2], [3, 7], [1, 6], [0, 4], [8, 1], [5, 6], [2, 1], [8, 3], [6, 4], [8, 6], [2, 7], [6, 6], [8, 7], [1, 8], [7, 4], [4, 7], [4, 5], [8, 4], [6, 1], [2, 2], [1, 5], [7, 6], [3, 6], [5, 0], [4, 1], [2, 8], [6, 8], [3, 1], [5, 3], [3, 4], [7, 2], [2, 5], [8, 5], [5, 5], [7, 8], [8, 8], [6, 5], [6, 7], [4, 6], [2, 6], [3, 5], [2, 0], [5, 7], [1, 0], [0, 3], [2, 4], [7, 5], [8, 0], [7, 3], [0, 0], [3, 8], [5, 8], [3, 0], [1, 7], [1, 3], [4, 3]]
//Goes quickly
//let posOrder = [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4], [0, 5], [0, 6], [0, 7], [0, 8], [1, 0], [1, 1], [1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [1, 7], [1, 8], [2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5], [2, 6], [2, 7], [2, 8], [3, 0], [3, 1], [3, 2], [3, 3], [3, 4], [3, 5], [3, 6], [3, 7], [3, 8], [4, 0], [4, 1], [4, 2], [4, 3], [4, 4], [4, 5], [4, 6], [4, 7], [4, 8], [5, 0], [5, 1], [5, 2], [5, 3], [5, 4], [5, 5], [5, 6], [5, 7], [5, 8], [6, 0], [6, 1], [6, 2], [6, 3], [6, 4], [6, 5], [6, 6], [6, 7], [6, 8], [7, 0], [7, 1], [7, 2], [7, 3], [7, 4], [7, 5], [7, 6], [7, 7], [7, 8], [8, 0], [8, 1], [8, 2], [8, 3], [8, 4], [8, 5], [8, 6], [8, 7], [8, 8]]
const backtrack = (board) => {
const pos = posOrder.find(p => board[p[0]][p[1]] === 0)
if(!pos)
return true
const [row, col] = pos
return (shuffleArray([1, 2, 3, 4, 5, 6, 7, 8, 9]).some(number => {
if (!numberExists(board, number, row, col)) {
board[row][col] = number
if (backtrack(board))
return true
else {
board[row][col] = 0
return false
}
}
}))
}
const shuffleArray = (array) => {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1)); // This ; is necessary... apparently
[array[i], array[j]] = [array[j], array[i]]
}
return array
}
const numberInRow = (board, number, row) => board[row].some(col => col === number)
const numberInCol = (board, number, col) => board.some(row => row[col] === number)
const numberInRegion = (board, number, row, col) => {
const r = 3*Math.floor(row/3)
const c = 3*Math.floor(col/3)
return [board[r], board[r+1], board[r+2]].some(arr=> [arr[c], arr[c+1], arr[c+2]].some(nbr => nbr === number))
}
const numberExists = (board, number, row, col) => (
numberInRow(board, number, row) ||
numberInCol(board, number, col) ||
numberInRegion(board, number, row, col)
)
const sudokuBoard = [
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0]
]
backtrack(sudokuBoard)
Note that I'd prefer to randomize the order every time, just kept one randomized order to rerun the example.
In the non-random order, after 9 placements all combinations for the first row that would have duplicates, have already been eliminated. After 27 placements, there are already 3 lines and three 3x3 boxes completed. That means already some alternatives had to be considered at an early stage to make it work. For instance, there was only one possible digit to place at move 9.
The random order will in a first phase allow the placement of digits with much more liberty, as a lot of initial coordinates are not directly related, and so fewer choices have to be redone at an early stage. This means more wrong moves are left on the board for a longer time and will therefore take much more time before they are eventually undone and replaced by a different choice.
To get the better performance it is of utmost importance that early choices are good and don't have to be redone. So that means that the sudoku constraints have to be eagerly sought.
I would therefore say that the best order is to fill lines, columns and boxes as soon as possible. For instance, this seems to be a promising order:
The first line
The top-left box (so completing it with 6 more positions)
The second line (completing the 6 remaining positions)
The top-center box (completing 3 remaining positions)
The third line & top-right box (completing 3 remaining positions)
The first column (completing 6 remaining positions)
The second column (completing 6 remaining positions)
... etc
I have an object like this:
const arrays = {
one: [[1, 33, 41], [2, 0, 27], [3, 7, 9], [4, 1, 3]],
two: [[1, 77, 2], [2, 6, 3], [3, 0, 0], [4, 55, 3]],
three: [[1, 4, 6], [2, 0, 0], [3, 5, 6], [4, 0, 0]],
};
As you can see each first number is equal:
1 for each first inner array
2 for each second inner array
3 for each third inner array
etc...
I want to filter based on the first number of each array
and some comparator number e.g. [3]
If we have a filter number [3] (smaller or equal to 3),
the wanted result would be:
const arrays = {
one: [[1,33,41], [2,0,27], [3,7,9]],
two: [[1,77,2], [2,6,3], [3,0,0]],
three: [[1,4,6], [2,0,0], [3,5,6]],
};
Since all first numbers of inner arrays are smaller than or equal to 3.
The arrays starting with 4,5... are ignored.
What would be the ramda's way to have this functionality?
I like Ramda's map function because it can iterate over the properties of an object (and so avoid Object.fromEntries & Object.entries) and apply a function to each one of them. That function is filter which will take as argument the inner arrays. The function given to filter is itself a composition of gte and head; takes the first element of an array and compare it with 3:
const arrays =
{ one: [[1, 33, 41], [2, 0, 27], [3, 7, 9], [4, 1, 3]]
, two: [[1, 77, 2], [2, 6, 3], [3, 0, 0], [4, 55, 3]]
, three: [[1, 4, 6], [2, 0, 0], [3, 5, 6], [4, 0, 0]] };
map(filter(compose(gte(3), head)), arrays);
// ^ ^ ^ ^ ^
// A B C D E
//=> { one: [[ 1, 33, 41], [2, 0, 27], [3, 7, 9]]
//=> , two: [[ 1, 77, 2], [2, 6, 3], [3, 0, 0]]
//=> , three: [[ 1, 4, 6], [2, 0, 0], [3, 5, 6]] }
map over each property (A); each array is passed to filter (B)
Each inner array is passed to compose (C)
Take the head of each inner array (E) and compare with 3 (D)
Scott Christopher rightly pointed out in the comments that gte can be confusing when partially applied. In fact the whole composition can be replaced with this simple lambda: ([x]) => x <= 3.
Alternative solution which I like too:
map(filter(([x]) => x <= 3), arrays);
I'd totally subscribe for #customcommander's approach,
just wanted to add that you can also pass numerical indexes to R.propSatisfies.
const headIs3OrBelow = R.propSatisfies(R.gte(3), 0);
const fn = R.map(R.filter(headIs3OrBelow));
// ===
const data = {
one: [[1, 33, 41], [2, 0, 27], [3, 7, 9], [4, 1, 3]],
two: [[1, 77, 2], [2, 6, 3], [3, 0, 0], [4, 55, 3]],
three: [[1, 4, 6], [2, 0, 0], [3, 5, 6], [4, 0, 0]],
};
console.log(
fn(data),
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.js" integrity="sha512-3sdB9mAxNh2MIo6YkY05uY1qjkywAlDfCf5u1cSotv6k9CZUSyHVf4BJSpTYgla+YHLaHG8LUpqV7MHctlYzlw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
Also agree that gte and other similar methods are very difficult to read, because they kind of read backwards as is 3 gte than x... in Haskell you could do something like:
3 `gte` x
Vanilla approach:
const headIs3OrBelow = ([head]) => head <= 3;
const fn = (data) => Object.entries(data).reduce(
(res, [k, lists]) => ({ ...res, [k]: lists.filter(headIs3OrBelow) }),
{},
);
// ===
const data = {
one: [[1, 33, 41], [2, 0, 27], [3, 7, 9], [4, 1, 3]],
two: [[1, 77, 2], [2, 6, 3], [3, 0, 0], [4, 55, 3]],
three: [[1, 4, 6], [2, 0, 0], [3, 5, 6], [4, 0, 0]],
};
console.log(
fn(data),
);
I understand you would like to use Ramda, this is not a solution using the library but accomplishes the same. You could create an object from entries that are filtered by comparing the first array value a[0] to the maxVal passed to the function.
const arrays = {
one: [[1, 33, 41], [2, 0, 27], [3, 7, 9], [4, 1, 3]],
two: [[1, 77, 2], [2, 6, 3], [3, 0, 0], [4, 55, 3]],
three: [[1, 4, 6], [2, 0, 0], [3, 5, 6], [4, 0, 0]],
};
const filterArrays = (arsObj, maxVal) => {
return Object.fromEntries(Object.entries(arsObj).map(([k, v]) => {
return [k, v.filter((a) => a[0] <= maxVal)];
}));
}
const result = filterArrays(arrays, 3);
console.log(result);
I'm trying to write a function, that accepts an array of arrays as argument and filtering this array under certain condition. I will explain this condition later.
For example I have such array:
const arr = [
[1, 1, 20],
[2, 1, 15],
[3, 1.5, 15],
[4, 1, 15],
[5, 1, 20],
[6, 1.5, 15],
[7, 1, 25],
[8, 1, 15],
[9, 0, 15],
[10, 0, 15],
[11, 0, 15],
]
and the condition. I want to filter it by the value of the third column, but only if it's at least three in a sequence. The function should accept two arguments (or more if needed): value and array
So for example
const filterByValue = (array, val) => { //definition
return filtredArr
}
const newArr = filterByValue(array,15)
and now newArr should equal:
[
[2, 1, 15],
[3, 1.5, 15],
[4, 1, 15],
[8, 1, 15],
[9, 0, 15],
[10, 0, 15],
[11, 0, 15],
]
For now I only made:
const filterByValue = (arr, value) => {
const newArr = arr.filter((elem, index) => elem[2] === value)
return newArr
}
but this function returns
[
[2, 1, 15],
[3, 1.5, 15],
[4, 1, 15],
[6, 1.5, 15],
[8, 1, 15],
[9, 0, 15],
[10, 0, 15],
[11, 0, 15],
]
there shouldn't be [6,1.5,15]. I have no idea how it could be done to return only fragments of an array that contain at least three internal arrays in a row. Maybe any of you have any idea?
edit more explanation
This sequence means to me that I want only output which internal arrays that contain a set value in the third value (value argument in a function), but that they also have internal arrays that follow each other (at least three). Let's assume that the function accepts arr (first array in my post) and value = 15. The first internal array contains 20 as the third value, so it falls off. The second is okey, the third also and the fourth also - and should return in this situation 2,3,4 internal board (because they follow three in succession). Then the fifth value is 20, the sixth value is 15, but the next (seventh) value 25 again, so the function should not take into account the sixth table (because it is not followed by at least two tables that have the value 15). In the table 8,9,10,11 we have the value 15, and these are four tables (i.e. at least three). Therefore, the function should return an array containing the following tables 2,3,4 and 8,9,10,11. The sixth array contains the value 15, but not followed by two more that would have this value as well. The expected output is the second board in my post.
const filterByValue = (arr,value) => {
let newArray = [];
let cache = [];
arr.forEach(elem => {
if (elem[2] === value) {
cache.push(elem);
} else {
if (cache.length >= 3) {
newArray = newArray.concat(cache);
}
cache = [];
}
});
if (cache.length >= 3) {
newArray = newArray.concat(cache);
}
return newArray;
}
You could take a closure over an index and check if the values are in a sequence of three.
const
array = [[1, 1, 20], [2, 1, 15], [3, 1.5, 15], [4, 1, 15], [5, 1, 20], [6, 1.5, 15], [7, 1, 25], [8, 1, 15], [9, 0, 15], [10, 0, 15], [11, 0, 15]],
value = 15,
result = array.filter(
(index => ({ 2: v }, i, a) => {
if (v !== value) return false;
if (index === i || a[i + 1][2] === value && a[i + 2][2] === value) {
index = i + 1;
return true;
}
})
(-1)
);
console.log(result);
Try with filter and conditions based on whether match the value or not.
const arr = [
[1, 1, 20],
[2, 1, 15],
[3, 1.5, 15],
[4, 1, 15],
[5, 1, 20],
[6, 1.5, 15],
[7, 1, 25],
[8, 1, 15],
[9, 0, 15],
[10, 0, 15],
[11, 0, 15],
];
const filterByValue = (array, val) =>
array.filter((ar, i, items) => {
if (ar[2] !== val) {
return true;
} else {
return items[i - 1][2] === val || items[i + 1][2] === val;
}
});
console.log(filterByValue(arr, 15));
Without using the cache array.
Check if element contains number 15 if yes increment the counter if that counter reaches at least 3 it means you can store those 3 element into a new list. Use the the difference between the current index and the counter to get from the first sequence element to the last one. Repeat the process until end of the array is reached.
const arr = [
[1, 1, 20],
[2, 1, 15],
[3, 1.5, 15],
[4, 1, 15],
[5, 1, 20],
[6, 1.5, 15],
[7, 1, 25],
[8, 1, 15],
[9, 0, 15],
[10, 0, 15],
[11, 0, 15]
];
let cnt = 0;
let list = [];
for (let i = 0; i < arr.length; i++) {
// check if elements meet criteria
while (i < arr.length && arr[i][arr[i].length - 1] === 15) {
++i;
++cnt;
}
// acumulate into your array if at least 3 elm
if (cnt >= 3) {
for (let j = i - cnt; j < i; j++) {
list.push(arr[j]);
}
}
cnt = 0;
}
console.log(list)
Problem description:
The idea is to insert into existing intervals new interval which doesn't merge with existing intervals but fills the missing gaps between intervals. (This is not the interval merging problem)
For example, inserting interval [0, 7] to intervals [[0, 1], [3, 5]] would result new intervals with gaps filled [[0, 1], [1, 3], [3, 5], [5, 7]].
Interval range is already sorted smallest to larger [[0, 1], [3, 5]].
My current solution is a bit "broken", I ended up using too many if checks to cover some special cases which makes everything more complex then needed. I am looking for better ways to simplify the condition part. In the bottom of the code there are test cases included, also cases where my solution fails.
The test cases where my algorithm is failing and producing wrong results:
assert.deepEqual( // Broken
insertIntervalSec([[1, 5], [7, 10]], [4, 12]),
[[1, 5], [5, 7], [7, 10], [10, 12]],
);
assert.deepEqual(insertIntervalSec([[1, 1]], [1, 3]), [[1, 3]]); // Broken
function isOverLapping(a, b) {
return Math.max(a[0], b[0]) <= Math.min(a[1], b[1]);
}
function insertIntervalSec(arr, interval) {
const result = [];
let i = 0;
const contains = (a, b) => {
return a[0] >= b[0] && a[1] <= b[1]
};
if (arr.length <= 0) {
result.push(interval);
return result;
}
if (arr.length === 1 && contains(interval, arr[0])) {
result.push(interval);
return result;
}
// Start point
if (interval[1] >= arr[0][0] && isOverLapping(interval, arr[0])) {
result.push([interval[0], arr[0][0]]);
} else if (interval[1] <= arr[0][0]) {
result.push([interval[0], Math.min(interval[1], arr[0][0])]);
}
while (i < arr.length) {
const current = arr[i];
result.push(arr[i]);
if (!contains(interval, arr[i]) && isOverLapping(arr[i], interval)) {
const next = arr[i + 1];
// Special handling for the last item
if (next !== undefined) {
if (interval[1] > current[1]) {
result.push([current[1], next[0]]);
}
} else {
if (interval[0] <= current[0] && interval[1] <= current[1]) {
// TODO: No action
} else if (interval[0] >= current[0] || interval[1] >= current[0]) {
result.push([current[1], interval[1]]);
}
}
}
i++;
}
// End point
const len = arr.length;
const last = arr[len - 1];
if (last[1] <= interval[0] && !isOverLapping(last, interval)) {
result.push(interval);
}
return result;
}
assert.deepEqual(
insertIntervalSec([[1, 5], [10, 15], [20, 25]], [12, 27]),
[[1, 5], [10, 15], [15, 20], [20, 25], [25, 27]]
);
assert.deepEqual(
insertIntervalSec([[1, 5], [10, 15], [20, 25]], [-3, 0]),
[[-3, 0], [1, 5], [10, 15], [20, 25]]
);
assert.deepEqual(
insertIntervalSec([[1, 5], [10, 15], [20, 25]], [-3, 3]),
[[-3, 1], [1, 5], [10, 15], [20, 25]]
);
assert.deepEqual(
insertIntervalSec([[0, 5], [10, 15], [20, 25]], [15, 15]),
[[0, 5], [10, 15], [20, 25]]
);
assert.deepEqual(
insertIntervalSec([[0, 5], [10, 15], [20, 25]], [20, 21]),
[[0, 5], [10, 15], [20, 25]]
);
assert.deepEqual(
insertIntervalSec([[0, 5], [10, 15], [20, 25]], [26, 27]),
[[0, 5], [10, 15], [20, 25], [26, 27]]
);
assert.deepEqual(
insertIntervalSec([[0, 5], [10, 15], [20, 25]], [25, 27]),
[[0, 5], [10, 15], [20, 25], [25, 27]]
);
assert.deepEqual(insertIntervalSec([], [25, 27]), [[25, 27]]);
assert.deepEqual(insertIntervalSec([[1, 1]], [1, 1]), [[1, 1]]);
assert.deepEqual( // Broken
insertIntervalSec([[1, 5], [7, 10]], [4, 12]),
[[1, 5], [5, 7], [7, 10], [10, 12]],
);
assert.deepEqual(insertIntervalSec([[1, 1]], [1, 3]), [[1, 3]]); // Broken
assert.deepEqual(
insertIntervalSec2([[5, 5]], [6, 6]),
[[5, 5], [6, 6]]
);
assert.deepEqual(
insertIntervalSec2([[1, 3]], [6, 6]),
[[1, 3], [6, 6]]
);
With the exception of the last test case (see comment on question), this passes all the tests. The basic idea is you just keep track of start variable that indicated where how much of the inserted range you have used. This allows you to narrow it down to three cases:
the inserted interval fits entirely before the the current item
the current item in the iteration fits completely before the inserted interval
the item in the iteration overlaps.
After iterating the items, you can check if the inserted range has anything left to insert:
function insertIntervalSec(arr, insert) {
let start = insert[0]
let res = []
for (i = 0; i < arr.length; i++) {
let a = arr[i]
// smaller item in range
if (a[0] <= start) {
res.push(a)
start = Math.max(a[1], start)
continue
}
// moved past inserted interval add rest of arr
if (start >= insert[1]) {
res.push(...arr.splice(i))
break
}
// fill in spaces
let end = Math.min(insert[1], a[0])
res.push([start, end], a)
start = a[1]
}
// clean up left over range
if (start < insert[1]) res.push([start, insert[1]])
return res
}
console.log(insertIntervalSec([ [1, 5],[10, 15],[20, 25]], [-2, 27]))
I have an array with length 9. I want to remove elem from array.
arr = [
[0, 0],
[0, 1],
[0, 2],
[1, 0],
[1, 1],
[1, 2],
[2, 0],
[2, 1],
[2, 2]
]
let elem = [2, 0];
let index = arr.indexOf(elem)
if (index !== -1) {
arr.splice(arr.indexOf(elem), 1)
}
console.log(arr)
Why my splice does not work?
It is not possible to use Array#indexOf with another object reference than the same object reference. This means, if you have another array to check against, you need to iterate it element by element. For this, you could use Array#findIndex and iterate all elements with Array#every.
For splicing, you need the found index, and not to find another index, because you have it already.
var arr = [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]],
elem = [2, 0],
index = arr.findIndex(a => a.every((v, i) => elem[i] === v));
console.log(index);
if (index !== -1) {
arr.splice(index, 1);
}
console.log(arr);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use .filter() instead: (more about .filter() here)
arr = [
[0, 0],
[0, 1],
[0, 2],
[1, 0],
[1, 1],
[1, 2],
[2, 0],
[2, 1],
[2, 2]
]
let elem = [2, 0];
const newArray = arr.filter(item => !(item[0] === elem[0] && item[1] === elem[1]));
console.log(newArray)
.as-console-wrapper { max-height: 100% !important; top: 0; }
This simply checks if both parts of the array match, then reverses the result, so only those that DON'T match are returned
Not that it makes much difference nowadays but .filter() is also faster - Here's a test I ran on JSPerf
You can use the filter function
arr = [
[0, 0],
[0, 1],
[0, 2],
[1, 0],
[1, 1],
[1, 2],
[2, 0],
[2, 1],
[2, 2]
]
let m = [];
let elem = [2, 0];
let __j = elem.toString();
let k = arr.filter(function(item) {
let __i = item.toString();
return __i !== __j
})
console.log(k)
You can use JSON object to remove quickly a element:
arr = [
[0, 0],
[0, 1],
[0, 2],
[1, 0],
[1, 1],
[1, 2],
[2, 0],
[2, 1],
[2, 2]
]
let elem = [2, 0];
var arrjson = arr.map(JSON.stringify);
var elemjson = [elem].map(JSON.stringify);
//console.log(arrjson); //console.log(elemjson);
var index = arrjson.indexOf(elemjson[0]);
arrjson.splice(index,1);
//console.log(arrjson);
var arrresult = arrjson.map(JSON.parse);
console.log(arrresult);