I recently received some excellent advice for writing some jquery functionality. I am trying to turn it into a pluggin.
this is the code that I got from stackOverflow#micha at this link
https://stackoverflow.com/a/8820946/729820
$('#DischargeDateTimeMask').keypress(function (e) {
var regex = ["[0-1]",
"[0-2]",
":",
"[0-5]",
"[0-9]",
"(\\s)",
"(A|P)",
"M"],
string = $(this).val() + keyboard(e),
b = true;
for (var i = 0; i < string.length; i++) {
if (!new RegExp("^" + regex[i] + "$").test(string[i])) {
b = false;
}
}
return b;
});
function keyboard(a) { var b = a.charCode ? a.charCode : a.keyCode ? a.keyCode : 0; if (b == 8 || b == 9 || b == 13 || b == 35 || b == 36 || b == 37 || b == 39 || b == 46) { if ($.browser.mozilla) { if (a.charCode == 0 && a.keyCode == b) { return true } } } return String.fromCharCode(b) }
I personally attempt to turn it into a pluggin like so...
(function ($) {
$.fn.simpleTimeMask = function () {
$(this).keypress(function (e) {
debugger;
var regex = ["[0-2]",
"[0-4]",
":",
"[0-6]",
"[0-9]",
"(A|P)",
"M"],
string = $(this).val() + keyboard(e),
b = true;
for (var i = 0; i < string.length; i++) {
if (!new RegExp("^" + regex[i] + "$").test(string[i])) {
b = false;
}
}
return b;
});
}
(function ($) {
var methods = {
keyboard: function (a) {
//THIS
var b = a.charCode ? a.charCode : a.keyCode ? a.keyCode : 0; if (b == 8 || b == 9 || b == 13 || b == 35 || b == 36 || b == 37 || b == 39 || b == 46) { if ($.browser.mozilla) { if (a.charCode == 0 && a.keyCode == b) { return true } } } return String.fromCharCode(b)
}
}
});
function keyboard(a) { var b = a.charCode ? a.charCode : a.keyCode ? a.keyCode : 0; if (b == 8 || b == 9 || b == 13 || b == 35 || b == 36 || b == 37 || b == 39 || b == 46) { if ($.browser.mozilla) { if (a.charCode == 0 && a.keyCode == b) { return true } } } return String.fromCharCode(b) }
})(jQuery);
Notice how I have two functions that attempt to do the same thing. One I think would be a function that would be called by a user of this pluggin, the other would be used internally. Anyway, I got this thing all compiled and I attempted to add it to my project, but as expected, it did not work. What would be your recommendations for building this pluggin?
Related
We have logging tables in our Oracle database that show old and new values when changes are made to certain data fields. One of these fields is a SQL_TEXT field (for running reports via dynamic SQL).
For example:
CREATE TABLE schema1.rpt_sql_log (
rpt_id NUMBER(9), -- surrogate key of report
chg_id NUMBER(9), -- change number of query for this rpt_id
old_sql_txt VARCHAR2(2000), -- old report SQL statement
new_sql_txt VARCHAR2(2000) -- new report SQL statement
);
INSERT INTO schema1.rpt_sql_log VALUES (
1,
2,
'SELECT emp_id, dept_id, salary FROM emp ORDER BY emp_id',
'SELECT emp_id, dept_nm, salary FROM emp e INNER JOIN dept d ON e.dept_id = d.dept_id ORDER BY emp_id'
);
COMMIT;
Our analyst ordinarily would export these fields to 2 separate text files and then open them in BeyondCompare, UltraCompare, or WinMerge. They have asked if it is possible to do this textual comparison/diff directly in a query (i.e., is there a built-in diff function). To the best of my knowledge, no such built-in function exists.
My question is this: is there already a "diff-like" function available online that mimics the output of a Myers diff algorithm (i.e., something that highlights or <tag>s the changes, as one might see in Git or TortoiseSVN)
To my knowledge, there is no such built-in PL/SQL function to do so.
However, using this JavaScript code which describes a diff function that adds and tags and the MultiLingual Engine capabilities of the 21c Oracle database release (which allows you to create stored procedures that can execute JavaScript code); you could go with such an approach:
set define off
set serveroutput on size unlimited
DECLARE
ctx DBMS_MLE.context_handle_t := DBMS_MLE.create_context();
codeSnippet clob ;
result clob;
BEGIN
codeSnippet := q'~
/*
* Javascript Diff Algorithm
* By John Resig (http://ejohn.org/)
* Modified by Chu Alan "sprite"
*
* Released under the MIT license.
*
* More Info:
* http://ejohn.org/projects/javascript-diff-algorithm/
*/
function escape(s) {
var n = s;
n = n.replace(/&/g, "&");
n = n.replace(/</g, "<");
n = n.replace(/>/g, ">");
n = n.replace(/"/g, """);
return n;
}
function diffString( o, n ) {
o = o.replace(/\s+$/, '');
n = n.replace(/\s+$/, '');
var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/) );
var str = "";
var oSpace = o.match(/\s+/g);
if (oSpace == null) {
oSpace = ["\n"];
} else {
oSpace.push("\n");
}
var nSpace = n.match(/\s+/g);
if (nSpace == null) {
nSpace = ["\n"];
} else {
nSpace.push("\n");
}
if (out.n.length == 0) {
for (var i = 0; i < out.o.length; i++) {
str += '<del>' + escape(out.o[i]) + oSpace[i] + "</del>";
}
} else {
if (out.n[0].text == null) {
for (n = 0; n < out.o.length && out.o[n].text == null; n++) {
str += '<del>' + escape(out.o[n]) + oSpace[n] + "</del>";
}
}
for ( var i = 0; i < out.n.length; i++ ) {
if (out.n[i].text == null) {
str += '<ins>' + escape(out.n[i]) + nSpace[i] + "</ins>";
} else {
var pre = "";
for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {
pre += '<del>' + escape(out.o[n]) + oSpace[n] + "</del>";
}
str += " " + out.n[i].text + nSpace[i] + pre;
}
}
}
return str;
}
function randomColor() {
return "rgb(" + (Math.random() * 100) + "%, " +
(Math.random() * 100) + "%, " +
(Math.random() * 100) + "%)";
}
function diffString2( o, n ) {
o = o.replace(/\s+$/, '');
n = n.replace(/\s+$/, '');
var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/) );
var oSpace = o.match(/\s+/g);
if (oSpace == null) {
oSpace = ["\n"];
} else {
oSpace.push("\n");
}
var nSpace = n.match(/\s+/g);
if (nSpace == null) {
nSpace = ["\n"];
} else {
nSpace.push("\n");
}
var os = "";
var colors = new Array();
for (var i = 0; i < out.o.length; i++) {
colors[i] = randomColor();
if (out.o[i].text != null) {
os += '<span style="background-color: ' +colors[i]+ '">' +
escape(out.o[i].text) + oSpace[i] + "</span>";
} else {
os += "<del>" + escape(out.o[i]) + oSpace[i] + "</del>";
}
}
var ns = "";
for (var i = 0; i < out.n.length; i++) {
if (out.n[i].text != null) {
ns += '<span style="background-color: ' +colors[out.n[i].row]+ '">' +
escape(out.n[i].text) + nSpace[i] + "</span>";
} else {
ns += "<ins>" + escape(out.n[i]) + nSpace[i] + "</ins>";
}
}
return { o : os , n : ns };
}
function diff( o, n ) {
var ns = new Object();
var os = new Object();
for ( var i = 0; i < n.length; i++ ) {
if ( ns[ n[i] ] == null )
ns[ n[i] ] = { rows: new Array(), o: null };
ns[ n[i] ].rows.push( i );
}
for ( var i = 0; i < o.length; i++ ) {
if ( os[ o[i] ] == null )
os[ o[i] ] = { rows: new Array(), n: null };
os[ o[i] ].rows.push( i );
}
for ( var i in ns ) {
if ( ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1 ) {
n[ ns[i].rows[0] ] = { text: n[ ns[i].rows[0] ], row: os[i].rows[0] };
o[ os[i].rows[0] ] = { text: o[ os[i].rows[0] ], row: ns[i].rows[0] };
}
}
for ( var i = 0; i < n.length - 1; i++ ) {
if ( n[i].text != null && n[i+1].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&
n[i+1] == o[ n[i].row + 1 ] ) {
n[i+1] = { text: n[i+1], row: n[i].row + 1 };
o[n[i].row+1] = { text: o[n[i].row+1], row: i + 1 };
}
}
for ( var i = n.length - 1; i > 0; i-- ) {
if ( n[i].text != null && n[i-1].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null &&
n[i-1] == o[ n[i].row - 1 ] ) {
n[i-1] = { text: n[i-1], row: n[i].row - 1 };
o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1 };
}
}
return { o: o, n: n };
}
var bindings = require("mle-js-bindings");
var string1 = bindings.importValue("string1");
var string2 = bindings.importValue("string2");
bindings.exportValue("result", diffString(string1,string2)); ~';
dbms_mle.export_to_mle(ctx, 'string1', 'The red brown fox jumped over the rolling log.');
dbms_mle.export_to_mle(ctx, 'string2', 'The brown spotted fox leaped over the rolling log');
DBMS_MLE.eval(ctx, 'JAVASCRIPT', codeSnippet);
DBMS_MLE.import_from_mle(ctx, 'result', result);
DBMS_OUTPUT.put_line(result);
DBMS_MLE.drop_context(ctx);
END;
/
Running the code above using SQLcl, gives me:
The <del>red </del> brown <ins>spotted </ins> fox <del>jumped </del><ins>leaped
</ins> over the rolling <del>log.
</del><ins>log
</ins>
PL/SQL procedure successfully completed.
SQL>
I am writing an app which compares a sign on time to a block of times and then takes the number of sectors and then returns a duty limit.
For example, a sign on at 0035 for two sectors returns a duty limit of 1030.
This works OK in my Playground but brings up several compiler errors when transferred to Xcode.
Starting with "Operators are only allowed at global scope".
Can anyone point me in the right direction?
This is my code so far:
let dateMaker = NSDateFormatter()
dateMaker.dateFormat = "HHmm"
let FTL1 = dateMaker.dateFromString("0800")!
let FTL2 = dateMaker.dateFromString("1259")!
let FTL3 = dateMaker.dateFromString("1300")!
let FTL4 = dateMaker.dateFromString("1759")!
let FTL5 = dateMaker.dateFromString("1800")!
let FTL6 = dateMaker.dateFromString("2159")!
let FTL7 = dateMaker.dateFromString("2200")!
let FTL8 = dateMaker.dateFromString("2359")!
let FTL9 = dateMaker.dateFromString("0000")!
let FTL10 = dateMaker.dateFromString("0759")!
let SO = dateMaker.dateFromString("0035")
var sect = 2
func <(lhs: NSDate, rhs: NSDate) -> Bool
{
return lhs.compare(rhs) == .OrderedAscending
}
func >(lhs: NSDate, rhs: NSDate) -> Bool
{
return lhs.compare(rhs) == .OrderedDescending
}
if SO! == FTL1 || SO! == FTL2 || SO! > FTL1 && SO! < FTL2 {
if sect == 1 {
let FTL = "1400"
}
if sect == 2 {
let FTL = "1315"
}
if sect == 3 {
let FTL = "1230"
}
}
if SO! == FTL3 || SO! == FTL4 || SO! > FTL3 && SO! < FTL4 {
if sect == 1 {
let FTL = "1300"
}
if sect == 2 {
let FTL = "1215"
}
if sect == 3 {
let FTL = "1130"
}
}
if SO! == FTL5 || SO! == FTL6 || SO! > FTL5 && SO! < FTL6 {
if sect == 1 {
let FTL = "1200"
}
if sect == 2 {
let FTL = "1115"
}
if sect == 3 {
let FTL = "1030"
}
}
if SO! == FTL7 || SO! == FTL8 || SO! > FTL7 && SO! < FTL8 {
if sect == 1 {
let FTL = "1100"
}
if sect == 2 {
let FTL = "1030"
}
if sect == 3 {
let FTL = "0930"
}
}
if SO! == FTL9 || SO! == FTL10 || SO! > FTL9 && SO! < FTL10 {
if sect == 1 {
let FTL = "1100"
}
if sect == 2 {
let FTL = "1030"
}
if sect == 3 {
let FTL = "0930"
}
}
you should write code out of class
class NewObject {
}
func >(lhs: NSDate, rhs: NSDate) -> Bool
{
return lhs.compare(rhs) == .OrderedDescending
}
I am sorting a grid with knockout.js. Following is my sort function
this.sortByName = function() {
var event = arguments[1];
var targeElement = event.originalTarget;
// console.info(targeElement);
console.log(targeElement.attributes[1].nodeValue);
order = 'sorting';
configuration.data.sort(function(a, b) {
if(a.name<b.name){
order = 'sorting_desc';
return a.name > b.name ? -1 : 1;
}
else if(a.name>b.name){
order = 'sorting_asc'
return a.name < b.name ? -1 : 1;
}
});
$(targeElement).removeClass('sorting_asc sorting_desc').addClass(order);
};
The by default grid view
The Sorted image 1
The sorted image 2
As you can see the order of sorting is not correct. I have been playing around for 3 days in this issue.
Well I found a solution to this problem
this.sortByName = function() {
currentOrder = arguments[0].sortClass();
var sortColumn = arguments[0].rowText;
if(currentOrder =='sorting' || currentOrder =='sorting_desc'){
currentOrder='sorting_asc';
configuration.data.sort(function(a, b) {
if (a[sortColumn] == b[sortColumn])
return 0;
else if (a[sortColumn] < b[sortColumn])
return -1;
else
return 1;
});
}else{
currentOrder='sorting_desc';
configuration.data.sort(function(a, b) {
if (a[sortColumn] == b[sortColumn])
return 0;
else if (a[sortColumn] > b[sortColumn])
return -1;
else
return 1;
});
}
self.resetSortColumns();
arguments[0].sortClass(currentOrder);
};
I wrote a small function that splits a number to commas but I have to run over the number too many times. Can you sugest me a better way to do this?
public static function getCommaString(value:Number):String {
var stringValue:String = value.toString();
if (stringValue.length <= 3) {
return stringValue;
}
var i:int = stringValue.length % 3;
if (i == 0) {
i = 3;
}
for (; i < stringValue.length; i += 4 ) {
var part1:String = stringValue.substr(0, i);
var part2:String = stringValue.substr(i, stringValue.length);
stringValue = part1.concat(",", part2);
}
return stringValue;
}
I put together a testing class that compares your solution and others, including a solution from this blog post and solutions from this related StackExchange question and formatted them in a consistent way for easy comparison:
package
{
public class CommaNumberSolutions
{
public static function commaify( input:Number ):String
{
var split:Array = input.toString().split( '.' ),
front:String = split[0],
back:String = ( split.length > 1 ) ? "." + split[1] : null,
n:int = input < 0 ? 2 : 1,
commas:int = Math.floor( (front.length - n) / 3 ),
i:int = 1;
for ( ; i <= commas; i++ )
{
n = front.length - (3 * i + i - 1);
front = front.slice( 0, n ) + "," + front.slice( n );
}
if ( back )
return front + back;
else
return front;
}
public static function getCommaString( input:Number ):String
{
var s:String = input.toString();
if ( s.length <= 3 )
return s;
var i:int = s.length % 3;
if ( i == 0 )
i = 3;
for ( ; i < s.length; i += 4 )
{
var part1:String = s.substr(0, i);
var part2:String = s.substr(i, s.length);
s = part1.concat(",", part2);
}
return s;
}
public static function formatNumber( input:Number ):String
{
var s:String = input.toString()
var result:String = ''
while ( s.length > 3 )
{
var chunk:String = s.substr(-3)
s = s.substr(0, s.length - 3)
result = ',' + chunk + result
}
if ( s.length > 0 )
result = s + result
return result
}
public static function commaCoder( input:Number ):String
{
var s:String = "";
var len:Number = input.toString().length;
for ( var i:int = 0; i < len; i++ )
{
if ( (len-i) % 3 == 0 && i != 0)
s += ",";
s += input.toString().charAt(i);
}
return s;
}
public static function regex1( input:Number ):String
{
return input.toString().replace( /-{0,1}(\d)(?=(\d\d\d)+$)/g, "$1," );
}
public static function regex2( input:Number ):String
{
return input.toString().replace( /-{0,1}\d{1,3}(?=(\d{3})+(?!\d))/g , "$&,")
}
public static function addCommas( input:Number ):String
{
var negative:String = "";
if ( input < 0 )
{
negative = "-";
input = Math.abs(input);
}
var s:String = input.toString();
var results:Array = s.split(/\./);
s = results[0];
if ( s.length > 3 )
{
var mod:Number = s.length % 3;
var output:String = s.substr(0, mod);
for ( var i:Number = mod; i < s.length; i += 3 )
{
output += ((mod == 0 && i == 0) ? "" : ",") + s.substr(i, 3);
}
if ( results.length > 1 )
{
if ( results[1].length == 1 )
return negative + output + "." + results[1] + "0";
else
return negative + output + "." + results[1];
}
else
return negative + output;
}
if ( results.length > 1 )
{
if ( results[1].length == 1 )
return negative + s + "." + results[1] + "0";
else
return negative + s + "." + results[1];
}
else
return negative + s;
}
}
}
You can see a more detailed analysis at my other answer but you can compare the compactness of each solution and weigh that against their accuracy and performance, however for the sake of brevity this solution is the most accurate and performant (though not the most compact):
public function commaify( input:Number ):String
{
var split:Array = input.toString().split( '.' ),
front:String = split[0],
back:String = ( split.length > 1 ) ? "." + split[1] : null,
n:int = input < 0 ? 2 : 1,
commas:int = Math.floor( (front.length - n) / 3 ),
i:int = 1;
for ( ; i <= commas; i++ )
{
n = front.length - (3 * i + i - 1);
front = front.slice( 0, n ) + "," + front.slice( n );
}
if ( back )
return front + back;
else
return front;
}
Consider a MxN bitmap where the cells are 0 or 1. '1' means filled and '0' means empty.
Find the number of 'holes' in the bitmap, where a hole is a contiguous region of empty cells.
For example, this has two holes:
11111
10101
10101
11111
... and this has only one:
11111
10001
10101
11111
What is the fastest way, when M and N are both between 1 and 8?
Clarification: diagonals are not considered contiguous, only side-adjacency matters.
Note: I am looking for something that takes advantage of the data format. I know how to transform this into a graph and [BD]FS it but that seems overkill.
You need to do connected component labeling on your image. You can use the Two-pass algorithm described in the Wikipedia article I linked above. Given the small size of your problem, the One-pass algorithm may suffice.
You could also use BFS/DFS but I'd recommend the above algorithms.
This seems like a nice use of the disjoint-set data structure.
Convert the bitmap to a 2d array
loop through each element
if the current element is a 0, merge it with the set of one its 'previous' empty neighbors (already visited)
if it has no empty neighbors, add it to its own set
then just count the number of sets
There may be advantages gained by using table lookups and bitwise operations.
For example whole line of 8 pixels may be looked up in 256 element table, so number of holes in a field 1xN is got by single lookup. Then there may be some lookup table of 256xK elements, where K is number of hole configurations in previous line, contatining number of complete holes and next hole configuration. That's just an idea.
I wrote an article describe the answer on Medium https://medium.com/#ahmed.wael888/bitmap-holes-count-using-typescript-javascript-387b51dd754a
but here is the code, the logic isn't complicated and you can understand it without reading the article.
export class CountBitMapHoles {
bitMapArr: number[][];
holesArr: Hole[] = [];
maxRows: number;
maxCols: number;
constructor(bitMapArr: string[] | number[][]) {
if (typeof bitMapArr[0] == 'string') {
this.bitMapArr = (bitMapArr as string[]).map(
(word: string): number[] => word.split('').map((bit: string): number => +bit))
} else {
this.bitMapArr = bitMapArr as number[][]
}
this.maxRows = this.bitMapArr.length;
this.maxCols = this.bitMapArr[0].length;
}
moveToDirection(direction: Direction, currentPosition: number[]) {
switch (direction) {
case Direction.up:
return [currentPosition[0] - 1, currentPosition[1]]
case Direction.down:
return [currentPosition[0] + 1, currentPosition[1]]
case Direction.right:
return [currentPosition[0], currentPosition[1] + 1]
case Direction.left:
return [currentPosition[0], currentPosition[1] - 1]
}
}
reverseDirection(direction: Direction) {
switch (direction) {
case Direction.up:
return Direction.down;
case Direction.down:
return Direction.up
case Direction.right:
return Direction.left
case Direction.left:
return Direction.right
}
}
findNeighbor(parentDir: Direction, currentPosition: number[]) {
let directions: Direction[] = []
if (parentDir === Direction.root) {
directions = this.returnAvailableDirections(currentPosition);
} else {
this.holesArr[this.holesArr.length - 1].positions.push(currentPosition)
directions = this.returnAvailableDirections(currentPosition).filter((direction) => direction != parentDir);
}
directions.forEach((direction) => {
const childPosition = this.moveToDirection(direction, currentPosition)
if (this.bitMapArr[childPosition[0]][childPosition[1]] === 0 && !this.checkIfCurrentPositionExist(childPosition)) {
this.findNeighbor(this.reverseDirection(direction), childPosition)
}
});
return
}
returnAvailableDirections(currentPosition: number[]): Direction[] {
if (currentPosition[0] == 0 && currentPosition[1] == 0) {
return [Direction.right, Direction.down]
} else if (currentPosition[0] == 0 && currentPosition[1] == this.maxCols - 1) {
return [Direction.down, Direction.left]
} else if (currentPosition[0] == this.maxRows - 1 && currentPosition[1] == this.maxCols - 1) {
return [Direction.left, Direction.up]
} else if (currentPosition[0] == this.maxRows - 1 && currentPosition[1] == 0) {
return [Direction.up, Direction.right]
} else if (currentPosition[1] == this.maxCols - 1) {
return [Direction.down, Direction.left, Direction.up]
} else if (currentPosition[0] == this.maxRows - 1) {
return [Direction.left, Direction.up, Direction.right]
} else if (currentPosition[1] == 0) {
return [Direction.up, Direction.right, Direction.down]
} else if (currentPosition[0] == 0) {
return [Direction.right, Direction.down, Direction.left]
} else {
return [Direction.right, Direction.down, Direction.left, Direction.up]
}
}
checkIfCurrentPositionExist(currentPosition: number[]): boolean {
let found = false;
return this.holesArr.some((hole) => {
const foundPosition = hole.positions.find(
(position) => (position[0] == currentPosition[0] && position[1] == currentPosition[1]));
if (foundPosition) {
found = true;
}
return found;
})
}
exec() {
this.bitMapArr.forEach((row, rowIndex) => {
row.forEach((bit, colIndex) => {
if (bit === 0) {
const currentPosition = [rowIndex, colIndex];
if (!this.checkIfCurrentPositionExist(currentPosition)) {
this.holesArr.push({
holeNumber: this.holesArr.length + 1,
positions: [currentPosition]
});
this.findNeighbor(Direction.root, currentPosition);
}
}
});
});
console.log(this.holesArr.length)
this.holesArr.forEach(hole => {
console.log(hole.positions)
});
return this.holesArr.length
}
}
enum Direction {
up = 'up',
down = 'down',
right = 'right',
left = 'left',
root = 'root'
}
interface Hole {
holeNumber: number;
positions: number[][]
}
main.ts file
import {CountBitMapHoles} from './bitmap-holes'
const line = ['1010111', '1001011', '0001101', '1111001', '0101011']
function main() {
const countBitMapHoles = new CountBitMapHoles(line)
countBitMapHoles.exec()
}
main()
function BitmapHoles(strArr) {
let returnArry = [];
let indexOfZ = [];
let subarr;
for(let i=0 ; i < strArr.length; i++){
subarr = strArr[i].split("");
let index = [];
for(let y=0 ; y < subarr.length; y++){
if(subarr[y] == 0)
index.push(y);
if(y == subarr.length-1)
indexOfZ.push(index);
}
}
for(let i=0 ; i < indexOfZ.length; i++){
for(let j=0; j<indexOfZ[i].length ; j++){
if(indexOfZ[i+1] && (indexOfZ[i][j]==indexOfZ[i+1][j] || indexOfZ[i+1].indexOf(indexOfZ[i][j])))
returnArry.indexOf(strArr[i]) < 0 ? returnArry.push(strArr[i]): false;
if(Math.abs(indexOfZ[i][j]-indexOfZ[i][j+1])==1)
returnArry.indexOf(strArr[i]) < 0 ? returnArry.push(strArr[i]): false;
}
}
return returnArry.length;
}
// keep this function call here
console.log(BitmapHoles(readline()));
function findHoles(map) {
let hole = 0;
const isHole = (i, j) => map[i] && map[i][j] === 0;
for (let i = 0; i < map.length; i++) {
for (let j = 0; j < map[i].length; j++) {
if (isHole(i, j)) {
markHole(i, j);
hole++;
}
}
}
function markHole(i, j) {
if (isHole(i, j)) {
map[i][j] = 2;
markHole(i, j - 1);
markHole(i, j + 1);
markHole(i + 1, j);
markHole(i - 1, j);
}
}
return hole;
}