I'm trying to create an Javascript card game but i need to match to 4 following up numbers in a list. But i always create some insane layered loop thing like:
cards = [{card:'h7'},{card:'c8'},{card:'h9'},{card:'st'}]
var sorted = ['7','8','9','t','j','q','k','a']
var found4 = false
for(var i =0;i < 5;i++){
var found = 0;
for(var j = 0;j < 4;j++){
for(var c in cards){
if(cards[c].card.charAt(1) == sorted[i+j]){
found++
}
}
}
if(found == 4){
found4 = true
}
}
Are there better ways to match an array?
some input examples:
'7','8','9','t' => true
'j','q','k','a' => true
'7','8','k','a' => false
'j','k','7','a' => false
(the input is not sorted)
You can write a prototype method for Array (You can refer to the following post) as
Array.prototype.contains = function(obj) {
var i = this.length;
while (i--) {
if (this[i] == obj) {
return i;
}
}
return false;
}
var sorted = ['7', '8', '9', 't', 'j', 'q', 'k', 'a']
function check(arr) {
index = sorted.contains(arr[0])
if (index === false) {
return false;
}
count = 1
for (var i = 1; i < 4; i++) {
sortedIndex = index + i > sorted.length ? index + i - sorted.length : index + i
if (sorted[sortedIndex] == arr[i]) count++;
}
if (count == 4) {
return true;
}
return false;
}
console.log(check(['j','q','k','a']))
you can see it work here
I would have separate fields for suit and value. This then makes it much easier to test if the values are in order. Note that the code below does not include range checking, or other validation, but I'm assuming that is taken care of.
// Suit is [c]lubs, [d]iamonds, [h]earts, or [s]pades
// Value is from Ace (1) to King (13). Jack is 11, and Queen is 12.
cards = [
{suit:'h', value: 7 } // 7 of hearts
{suit:'c', value: 8 } // 8 of clubs
{suit:'h', value: 9 } // 9 of hearts
{suit:'s', value: 10 } // Ten of spades
{suit:'s', value: 11 } // Jack of spades
]
if (cards.length <= 1)
{
// Having 0 or 1 cards means they are, by definition, in order.
return true;
}
// Test each card (starting with the second) to ensure that it is
// 1 greater than it's predecessor.
var previousValue = cards[0].value;
for(var i = 1; i < cards.length; i++){
if (previousValue + 1 != cards[i].value)
{
// This card is not the next card in sequence, so
// the hand is not in order.
return false;
}
}
return true;
First, your algorithm should work on all arrays (no fixed lengths etc), so lets get chars to find:
var tofind = cards.map(function(c){return c.card.charAt(1);});
When all your pieces have length one, there is a very simple function to help you:
return sorted.join("").indexOf(tofind.join(""))!=-1;
However, I don't understand your approach completely. This loop:
for (var c in cards)
if (cards[c].card.charAt(1) == sorted[i+j])
found++
seems odd to me. First, cards is an array, so don't use a for-in-loop. But if you search all cards for the current letter you want to match, how does this have anything to do with order?
A possible solution using Underscore and keeping your data structure
function test(seq,expected) {
var res=isSequence(seq);
if (res===expected)
console.log( seq.join(',')+" : success");
else
console.log( seq.join(',')+" : fail");
}
function isSequence(seq) {
var sorted = ['7','8','9','t','j','q','k','a'], l=seq.length, i, ix;
if (l===0) return true;
ix=_.indexOf(sorted, seq[0]);
if (ix===-1) return false;
if (ix>sorted.length-l) return false;
for (i=1;i<l;i++) {
if ( sorted[ix+i]!==seq[i] )
return false;
}
return true;
}
var cards = [{card:'h7'},{card:'c8'},{card:'h9'},{card:'st'}]
test( _.map(cards, function(obj) {
return obj.card.charAt(1);
}), true );
test(['7','8','9','t'] , true);
test(['j','q','k','a'] , true);
test(['7','8','k','a'] , false);
test(['j','k','7','a'] , false);
And a Fiddle http://jsfiddle.net/KDrDy/2/
Related
Let's say we have this string:
BBBBAAAABBAAAAAACCCCCBDDDDEEEEEEE,FFF
As you can see, here B is occurring 4 times at first but B is also present before DDDD.
Similarly, A is occurring 4 times at the beginning and later 6 times.
I want the expected output if I am searching B it should 4 times as the max occurrence B is 4. However if I am searching A then it should return 6 because the most occurrence for A is 6.
Here is my code I tried:
function checkRepeatativeString(str) {
let hashMap = {};
let seen = new Set();
let counter = 1;
let maxValue = 1;
let isPreviousValueSame = false;
let isNextValueSame = true;
for (let i = 0; i < str.length; i++) {
/**
* is previous value same
*/
if (str[i] == str[i-1]) {
isPreviousValueSame = true;
}
/**
* is next value same
*/
if (str[i] == str[i+1]) {
isNextValueSame = true;
}
if (seen.has(str[i]) && isPreviousValueSame) {
hashMap[str[i]][0]++;
hashMap[str[i]][1]++;
isPreviousValueSame = false;
} else if(seen.has(str[i]) && !isNextValueSame) {
maxValue = Math.max(hashMap[str[i]][1], maxValue);
counter = 0;
hashMap[str[i]] = [counter, maxValue];
} else {
maxValue = Math.max(maxValue, counter);
seen.add(str[i]);
hashMap[str[i]] = [counter, maxValue];
isPreviousValueSame = false;
}
}
return hashMap;
}
let str = "BBBBAAAABBAAAAAACCCCCBDDDDEEEEEEE,FFF";
console.log(checkRepeatativeString(str));
This code is working but if you look for B, I am getting stuck at the beginning of value.
My program returns out for B:
B: [ 1, 1 ]
^ ^
Inside array, 1 is a counter which scans the string and second 1 in array is a max value which should return the output. However my program is returning 1 for B. I am expecting 4 as max value.
Help would be appreciated~
Quick and dirty.
function maxConsecutiveCharacters(check, haystack) {
if(check.length !== 1) return false;
let result = 0;
let buffer = 0;
for(let i = 0; i < haystack.length; i++) {
if(haystack[i] === check) {
buffer++;
}
else {
if(buffer > result) {
result = buffer;
}
buffer = 0;
}
if(buffer > result) {
result = buffer;
}
}
return result;
}
That looks overly complicated. Consider approaching the problem from a different angle - first split up the string into segments of repeating characters, and group them into an object based on the length of the longest substring for a given character.
const checkRepeatativeString = (str) => {
const longestCounts = {};
for (const consecutive of (str.match(/(.)\1*/g) || [])) {
const char = consecutive[0];
longestCounts[char] = Math.max(
longestCounts[char] || 0, // Use the existing value in the object if it exists and is higher
consecutive.length // Otherwise, use the length of the string iterated over
);
}
return longestCounts;
};
let str = "BBBBAAAABBAAAAAACCCCCBDDDDEEEEEEE,FFF";
console.log(checkRepeatativeString(str));
Simpler code often means less surface area for bugs.
This is a typical Power Loop problem, and I only need a simple and elegant (compact) solution... I will show fist the sample of problem/solution with nested for loops. Suppose that I need to transform this piece of code into a recursion:
console.log("bits","Binary")
for (let i=0; i<2; i++) {
show(i)
for (let j=0; j<2; j++) {
show(i,j)
for (let k=0; k<2; k++)
show(i,j,k) // ... l,m,n,o,p
} // j
} // i
function show(...args) {
let code = String( args.reduce( (ac,cur) => ''+ac+cur ) )
console.log( code.length, code )
}
The 14-unique-lines output of this 3-level sample is
bits Binary
1 '0'
2 '00'
3 '000'
3 '001'
2 '01'
3 '010'
3 '011'
1 '1'
2 '10'
3 '100'
3 '101'
2 '11'
3 '110'
3 '111'
Ugly and partial solution
I am trying to solve using as reference this solution:
callManyTimes([2,2,2], show);
function callManyTimes(maxIndices, func) {
doCallManyTimes(maxIndices, func, [], 0);
}
function doCallManyTimes(maxIndices, func, args, index) {
if (maxIndices.length == 0) {
let x = args.slice(0); // cloning
while(x.length>0) {
func(x); // why send array[array]?
x.shift();
}
} else {
var rest = maxIndices.slice(1);
for (args[index] = 0; args[index] < maxIndices[0]; ++args[index]) {
doCallManyTimes(rest, func, args, index + 1);
}
}
}
function show(...args) {
if (typeof args[0] == 'object') args=args[0] // workaround... can optimize?
let code = String( args.reduce( (ac,cur) => ''+ac+cur ) )
console.log( code.length, code )
}
The output have duplicated lines, but there are a subset of lines that are the solution... So, seems near, but is ugly (no elegant use of the recurrence stack, etc.)
3 '000'
2 '00'
1 '0'
3 '001'
2 '01'
1 '1'
3 '010'
2 '10'
1 '0'
3 '011'
2 '11'
1 '1'
...
You could take a function which takes a temporary array for the generated values.
function show(...args) {
let code = args.join('');
console.log(code.length, code);
}
function callManyTimes(max, cb, items = []) {
var i, temp;
if (items.length === max.length) return;
for (i = 0; i < max[items.length]; i++) {
temp = items.concat(i);
cb(...temp);
callManyTimes(max, cb, temp);
}
}
callManyTimes([2, 2, 2], show);
A simple backtracking recursive function will visit these in the order of the first example with something like:
function iter(maxlength, cur = ''){
if (cur.length >= maxlength) return
for (let i = 0; i < 2; i++){
console.log(cur.length + 1 + ":", cur + i)
iter(maxlength, cur + i)
}
}
iter(3)
You can also generate an array with the same idea and a generator function (here it's returning an array of arrays to join later, but the same principle):
function* iter(maxlength, prefix = []){
if (prefix.length >= maxlength) return
for (let i = 0; i < 2; i++){
yield [i, ...prefix]
yield * iter(maxlength, [i, ...prefix])
}
}
console.log([...iter(3)].map(a => a.join(',')))
I think you are looking for
callManyTimes([2,2,2], show);
function callManyTimes(maxIndices, func, args=[]) {
if (maxIndices.length == 0) {
func(...args);
} else {
func(...args);
var rest = maxIndices.slice(1);
var index = args.length;
args = args.slice();
for (args[index] = 0; args[index] < maxIndices[0]; ++args[index]) {
callManyTimes(rest, func, args);
}
}
}
function show(...args) {
let code = args.join(" ");
console.log(args.length + ": "+ code )
}
Instead of that while loop, you want to call func only once. To get the partial results, you'd put a func call before the loop with the recursive calls, just like you did in your expanded version. I also eliminated the index parameter which is just the args.length, and made copies of args before adding a new level.
Also you should just use spread syntax for the call, as you receive the arguments in show with rest parameter syntax.
You can write the output by hand and use .repeat()
const f = (a,b,c=[a+b,b+a,a+b+a,a+a+b,a+b+b,b+a+b,b+b+a,b+a+a]) => {
for(let i=3;i;c.push(a.repeat(i),b.repeat(i)),i--);return c
};
console.log(f('0','1'));
How do i check that a given word is an isogram with pure javascript, using a function. the function must return true or false.
An isogram is a word with a repeated character.
I know this code works, but i need a better solution.
function isIsogram(word){
x = false; y = false;
for(i = 0; i < word.length; i++){
wordl = word.substring(0,i)
wordr = word.substring(i)
x = wordl.includes(word.charAt(i))
y = wordr.includes(word.charAt(i))
//console.log(x,wordl,wordr)
}
return x&&y
}
isIsogram("thomas");//False
isIsogram("moses"); //True
Remove the duplicate letter from string then check both length. if same its an isogram.
function isIsogram(str){
return str.split('').filter((item, pos, arr)=> arr.indexOf(item) == pos).length == str.length;
}
console.log(isIsogram('thomas'));
console.log(isIsogram('moses'));
One way of doing this!
function isIsogram(str){
return !str.match(/([a-z]).*\1/i);
}
Here is a simple approach using .split() and .every():
let isIsogram = (str) => str.split("").every((c, i) => str.indexOf(c) == i);
console.log(isIsogram("thomas")); /* no repeating letter */
console.log(isIsogram("moses")); /* s repeat 2 times */
console.log(isIsogram("hello")); /* l repeat 2 times */
console.log(isIsogram("world")); /* no repeating letter */
console.log(isIsogram("a b c")); /* space character repeat 2 times */
Docs:
String.prototype.split()
String.prototype.indexOf()
Array.prototype.every()
Building on kishea's answer:
function isIsogram(sWord)
{
for (iCharIndex = 0; iCharIndex < sWord.length; iCharIndex++)
if (sWord.substring(iCharIndex + 1).includes(sWord.charAt(iCharIndex)))
return false;
return true;
}
If the character at the current position (charAt) is found (includes) to the right of the current position (substring), false is returned. Otherwise the loop runs to the end and true is returned.
const isIsogram = (word) => {
return new Set(word.toLowerCase()).size === word.length
}
console.log(isIsogram('Thor'));//true
console.log(isIsogram('Loki'));//true
console.log(isIsogram('America'));//false
function isIsogram(str) {
return new Set(str.toUpperCase()).size == str.length
}
What about :
> function isIsogram(word) {
... var a = word.split('')
... var b = Array.from(new Set(a))
... return a.length === b.length;
... }
undefined
> isIsogram("mesos")
false
> isIsogram("thomas")
true
Or better (checking each char only once) :
> function isIsogram2(word) {
... for(var i=0,len=word.length-1;i<len;++i) {
..... var c = word[i]
..... if(word.indexOf(c,i+1) !== -1) return false;
..... }
... return true;
... }
undefined
> isIsogram2("mesos")
false
> isIsogram2("thomas")
true
function isIsogram(word){
return !/(.).*\1|\d/i.test(word)
}
var str=prompt('Enter a string');
var strlen=str.length;
for(i=0;i<strlen;i++){
var stc=str.charAt(i);
var flag=0;
for(j=0;j<strlen;j++){
var stk=str.charAt(j);
if(stc==stk){
flag=flag+1;
}
}
if(flag!=1){
break;
}
}
if(flag!=1){
alert('It is not an isogram');
}
else{
alert('It is an isogram');
}
While given a word, this function if splits the word into two,
That is wordl and wordr respectively.
Both splittings are checked to include a character in the original word. If wordl and wordr both contain any character in the original word. Then surely this is an isogram
function isIsogram(word){
x = false; y = false;
for(i = 0; i < word.length; i++){
wordl = word.substring(0,i)
wordr = word.substring(i)
x = wordl.includes(word.charAt(i))
y = wordr.includes(word.charAt(i))
//console.log(x,wordl,wordr)
}
return !x&&y
}
isIsogram("thomas");//True
isIsogram("moses"); //False
const isIsogram = (string) => {
const lowerCased = string.toLowerCase()
const result = lowerCased.split('').every((v,i)=>lowerCased.indexOf(v)===i)
return result
}
console.log(isIsogram('ambidExtRously')) // true
console.log(isIsogram('patteRN')) // false
function isIsogram(word) {
let uniqueCharacters = new Set(word.split(''));
uniqueCharacters = Array.from(uniqueCharacters); //get all the unique char
let charFreq = {}; //e.g {a:2, b:3}
for (element of uniqueCharacters) {
charFreq[element] = 0;
} //set the frequency of each char to zero
function updateFrequency(element) {
charFreq[element] += 1;
}//callback used directly below
word.split('').forEach(updateFrequency); //for each char encountered, update frequency
let frequencyOfCharacter = [];
for (keys in charFreq) {
frequencyOfCharacter.push(charFreq[keys]);
}
function equal(item) {
return item === frequencyOfCharacter[0];
}
//check if all the frequencies are the same, an isogram means all characters occur at the same frequency
return frequencyOfCharacter.every(equal);
}
console.log(isIsogram('try'), isIsogram('baba'), isIsogram('tests'));
var userInput = prompt('enter number here');
var number = new Array(userInput.toString().split(''));
if (number ????){ //checks if the number is in a continuous stream
alert(correct);
}
else{
alert(invalid);
}
In Javascript, what can I do at "????" to check if it is in a continuous order/stream? Also how can I do this so that it only checks for this order/stream after a specific index in the array? Meaning the user enters say "12345678901234" which would pop up correct, but "12347678901234" would pop up invalid?(note there are two 7's) For the second part "3312345678901234" would pop up correct, how can this be implemented?
You can make a function that checks any string for a stream of continuous/increasing alpha-numeric characters starting at a given index like this:
function checkContinuous(str, startIndex) {
startindex = startIndex || 0;
if (str.length <= startIndex) {
return false;
}
var last = str.charCodeAt(startIndex);
for (var i = startIndex + 1; i < str.length; i++) {
++last;
if (str.charCodeAt(i) !== last) {
return false;
}
}
return true;
}
If it's numbers only and wrapping from 9 back to 0 is considered continuous, then it's a little more complicated like this:
function checkContinuous(str, startIndex) {
// make sure startIndex is set to zero if not passed in
startIndex = startIndex || 0;
// skip chars before startIndex
str = str.substr(startIndex);
// string must be at least 2 chars long and must be all numbers
if (str.length < 2 || !/^\d+$/.test(str)) {
return false;
}
// get first char code in string
var last = str.charCodeAt(0);
// for the rest of the string, compare to last code
for (var i = 1; i < str.length; i++) {
// increment last charCode so we can compare to sequence
if (last === 57) {
// if 9, wrap back to 0
last = 48;
} else {
// else just increment
++last;
}
// if we find one char out of sequence, then it's not continuous so return false
if (str.charCodeAt(i) !== last) {
return false;
}
}
// everything was continuous
return true;
}
Working demo: http://jsfiddle.net/jfriend00/rHH4B/
No need for arrays, just back though the string one character at a time.
When you hit a 0, substitute 10, and continue until the number
is not one more than the previous one.
function continuousFromChar(str, start){
start= start || 0;
var i= 0, L= str.length, prev;
while(L){
c= +(str.charAt(-- L)) || 10; // use 10 for 0
prev=+(str.charAt(L- 1));
if(c-prev !== 1) break;
}
return start>=L;
}
var s= "3312345678901234";
continuousFromChar(s,2)
/* returned value: (Boolean)
true
*/
This will do the checking in real-time entry, but a similar principle could be used to check an entry on a button submit or similar. I was not 100% sure as to which way you wanted it, so I went for the live method.
HTML
<input id="stream" type="text" />
Javascript
window.addEventListener("load", function () {
document.getElementById("stream").addEventListener("keyup", function (evt) {
var target = evt.target;
var value = target.value;
var prev;
var last;
var expect;
target.value = value.replace(/[^\d]/, "");
if (value.length > 1) {
prev = parseInt(value.slice(-2, -1), 10);
last = parseInt(value.slice(-1), 10);
expect = prev + 1;
if (expect > 9) {
expect = 0;
}
if (last !== expect) {
target.value = value.slice(0, value.length - 1);
}
}
}, false);
});
On jsfiddle
By changing the value here
if (value.length > 1) {
You can change where the checking starts.
Update: Ok, so it is function that you want, and you insist that it splits the string into an array. Then using the above as a reference, you could convert it to something like this.
Javascript
window.addEventListener("load", function () {
var testStrings = [
"0123456789012",
"0123456789",
"0123455555",
"555012345678901234",
"0123455555"];
function test(string, offset) {
if (typeof string !== "string" || /[^\d]/.test(string)) {
return false;
}
var array = string.split("");
var prev;
var last;
var expect;
return !array.some(function (digit, index) {
if (index >= offset) {
prev = parseInt(array[index - 1], 10);
last = parseInt(digit, 10);
expect = prev + 1;
if (expect > 9) {
expect = 0;
}
if (last !== expect) {
return true;
}
}
return false;
});
}
testStrings.forEach(function (string) {
console.log(string, test(string, 1));
});
});
On jsfiddle
As your question does not fully specify all possibilities, the above will return true for an empty string (""), of course you can simply add a check at the very beginning for that.
I also do not perform any checking for a valid number for your offset, but again this is something simple that you can add.
Of course these are just one (two) of many possible solutions, but hopefully it will set your mind in the right direction of thought.
There are some good answers here, but I would like to show a slight variation. I think it is important to showcase some different aspects of JavaScript and separating interests in code.
Functions as first class objects are cool - the exact rules for "continuous" can be changed with only changing the predicate function. Perhaps we should allow skipping numbers? No problem. Perhaps we allow hex digits? No problem. Just change the appropriate follows function for the specific rules.
This can be implemented generically because strings support indexing. This will work just as well over other array-like objects with an appropriate follows function. Note that there are no string-specific functions used in the continuous function.
Code also on jsfiddle:
// returns true only iff b "follows" a; this can be changed
function follows_1Through9WithWrappingTo0(b,a) {
if (b === "1" && a === undefined) {
// start of sequence
return true;
} else if (b === "0" && a === "9") {
// wrap
return true;
} else {
// or whatever
return (+b) === (+a) + 1;
}
}
function continuous(seq, accordingTo, from) {
// strings can be treated like arrays; this code really doesn't care
// and could work with arbitrary array-like objects
var i = from || 0;
if ((seq.length - i) < 1) {
return true;
}
var a = undefined;
var b = undefined;
for (; i < seq.length; i++) {
b = seq[i];
if (!accordingTo(b, a)) {
return false; // not continuous
}
a = b;
}
return true;
}
function assert(label, expr, value) {
if (!(expr === value)) {
alert("FAILED: " + label);
}
}
var follows = follows_1Through9WithWrappingTo0;
assert("empty1", continuous("", follows), true);
assert("empty2", continuous("foobar", follows, 6), true);
assert("skip", continuous("331234", follows, 2), true);
assert("good 1", continuous("123456789", follows), true);
assert("good 2", continuous("12345678901234", follows), true);
assert("bad seq 1", continuous("12347678901234", follows), false);
assert("bad seq 2", continuous("10", follows), false);
// here a different predicate ensures all the elements are the same
var areAllSame = function (b, a) {
return a === undefined || a === b;
};
assert("same", continuous("aaaaa", areAllSame), true);
Note that the skipping could also be extracted out of the continuous function: in a language with better "functional" collection support, such as C#, this is exactly what I'd do first.
I have an array of objects gAllMedicalFilesClaimantsArray with 2 properties (UserID & UserInfo)
For example:
gAllMedicalFilesClaimantsArray[0].UserID = "111";
gAllMedicalFilesClaimantsArray[0].UserInfo = "AAA-111";
gAllMedicalFilesClaimantsArray[1].UserID = "222";
gAllMedicalFilesClaimantsArray[1].UserInfo = "BDD-478333";
What is the fastest way to check whether a specific UserID exists in the array using Jquery or Javascript because gAllMedicalFilesClaimantsArray has got 8000 records?
Thanks
var match = '222';
var matches = $.grep(myArray, function(el, index) {
return (el.UserID === match);
});
You can fasten the search process by using Binary Search algorithm if the array is sorted (e.g with respect to UserId).
function binarySearch(array, userid) {
var low = 0, high = array.length - 1,
i, comparison;
while (low <= high) {
i = parseInt((low + high) / 2, 10);
if (array[i].UserId < userid) { low = i + 1; continue; };
if (array[i].UserId > userid) { high = i - 1; continue; };
return array[i];
}
return null;
};
You can find the user of which ID is 12 by using the function:
var result = binarySearch(gAllMedicalFilesClaimantsArray, 12);
Something like this, I believe:
function exists(uid) {
var k = gAllMedicalFilesClaimantsArray.length;
uid = uid.toString(); // ensure the arg is a str (this can be omitted)
while (k--) {
if (gAllMedicalFilesClaimantsArray[k].UserID === uid) {
return true;
}
}
return false;
}
Is the array sorted by the UserID? If so, it can be improved either further by using a binary search; that would change this from O(n) to O(log n). Your example suggests it is. I found a good implementation of a binary search in JavaScript on the web, here. Here is the code if the site ever dies:
function binarySearch(items, value){
var startIndex = 0,
stopIndex = items.length - 1,
middle = Math.floor((stopIndex + startIndex)/2);
while(items[middle] != value && startIndex < stopIndex){
//adjust search area
if (value < items[middle]){
stopIndex = middle - 1;
} else if (value > items[middle]){
startIndex = middle + 1;
}
//recalculate middle
middle = Math.floor((stopIndex + startIndex)/2);
}
//make sure it's the right value
return (items[middle] != value) ? -1 : middle;
}
ExistsInArray(value, array){
for(var item in array){
if(item.UserId == value){
return true;
}
}
return false;
}
You can either prototype Array object, like this:
Array.prototype.exists = function(value, prop){
var i = null;
for (i in this)
if (this[i][prop] && this[i][prop] == value)
return true;
return false;
}
gAllMedicalFilesClaimantsArray.exists('222', 'UserID');