Yes. Although mine looks a bit uglier because I ran into an issue: if the string doesn't have Xs or Os, the result of match is null so it fails when trying to do .length (test checkXO("zpzpzpp") on your code).
I had to add the fallback into empty array for those cases. But I feel like there has to be a cleaner way of doing the same thing.
Yeah, I found the same issue when I tried to solve it in Codewars. So I had to do the same. Looking through other people's solutions, there's one that I find interesting that uses split() instead of a regular expression. Something like this:
The split approach is interesting. I was playing with different options, but not with this. Also, I found that using toLowerCase() made the code considerably slower, it works considerably faster if you use a regular expression in the split:
I think this is my favourite solution. I hadn't checked its speed, but I thought that toLowerCase() probably didn't make it any smoother than returning an empty array if there are no matches.
Gentoo Linux and VIM worshiper, C developer, network protocol dissector implementer,socket/network programmer, recently entered the embedded world, hater of buzzwords and made up titles
Sure, but this one is pretty easy to demystify, luckily: tr/set1/set2/ just replaces characters in set1 with those in set2 (positionally), and returns the number of characters replaced/deleted. So in this code, he just compares the number of X's replaced with the number of O's replaced, and includes the lower-case variants too.
The local($_) is just there to let him make the code more brief. $_ is the "default input/pattern-searching space", so its like the default argument that tr will search. But it's also a global, so this lets you make a local copy and set it to be the first argument to the function.
If he didn't do that, it might look like this:
sub xo{my$xos=shift;($xos=~tr/xX//)==($xos=~tr/oO//)}
Gentoo Linux and VIM worshiper, C developer, network protocol dissector implementer,socket/network programmer, recently entered the embedded world, hater of buzzwords and made up titles
#include<stdio.h>
#include<string.h>
#include<stdbool.h>// bool as string#define BOOL_STR(b) b ? "true" : "false"
// X/x checkstaticboolcheck_x(chard){if(d==88||d==120)returntrue;returnfalse;}// O/o checkstaticboolcheck_o(chard){if(d==79||d==111)returntrue;returnfalse;}staticboolcount_xo(constchar*d){// null pointerif(!d)returntrue;// get sizesize_tl=strlen(d);// no dataif(!l)returntrue;// res countersunsignedxc=0,oc=0;// mid pointunsignedmp=l/2,rm=l%2;// check for 88 (X) and 79 (O)// O(N/2)for(unsignedi=0,j=l-1;i<mp;i++,j--){// X counterxc+=check_x(d[i])+check_x(d[j]);// O counteroc+=check_o(d[i])+check_o(d[j]);}// remainderif(rm){// X counterxc+=check_x(d[mp+1]);// O counteroc+=check_o(d[mp+1]);}// resreturn(xc==oc?true:false);}intmain(void){// test stringsconstchar*str_00="ooxx";constchar*str_01="xooxx";constchar*str_02="ooxXm";constchar*str_03="zpzpzpp";constchar*str_04="zzoo";// resultsprintf("%s: %s\n",str_00,BOOL_STR(count_xo(str_00)));printf("%s: %s\n",str_01,BOOL_STR(count_xo(str_01)));printf("%s: %s\n",str_02,BOOL_STR(count_xo(str_02)));printf("%s: %s\n",str_03,BOOL_STR(count_xo(str_03)));printf("%s: %s\n",str_04,BOOL_STR(count_xo(str_04)));// no errorreturn0;}
Is it allowed to modify someone else’s solution? I think you made it a bit more complicated than necessary. Anyone please correct me if I’m wrong:
C strings end with the null character '\0', so you can skip getting the string length and do a while loop that checks for the null character (strlen does just that anyway);
I didn’t really understand why you’re walking the string from the start and the middle, instead of from beginning to end, so I changed it;
since we then only test for x and o once, I could inline the tests;
xc == oc and d == 88 || d == 120 are already boolean expressions, so you don’t need the ternary operator ... ? true : false or an if(...) return true; else return false;;
since I never used assertions in C, I took the opportunity to try them for the test cases (I intentionally wrote assert(count_xo("xooxx") == false) in the tests instead of assert(! count_xo("xooxx")) for consistency and readability.
#include <stdio.h>
#include <stdbool.h>
// bool as string#define BOOL_STR(b) b ? "true" : "false"
staticboolcount_xo(constchar*d){// null pointerif(!d)returntrue;// countersunsignedxc=0,oc=0,i=-1;while(d[++i]!='\0'){// X counterif(d[i]==88||d[i]==120)xc++;// O counterif(d[i]==79||d[i]==111)oc++;}// resreturnxc==oc;}intmain(void){// test stringsassert(count_xo("ooxx")==true);assert(count_xo("xooxx")==false);assert(count_xo("ooxXm")==true);assert(count_xo("zpzpzpp")==true);assert(count_xo("zzoo")==false);assert(count_xo("xoffxffo")==true);return0;}
Gentoo Linux and VIM worshiper, C developer, network protocol dissector implementer,socket/network programmer, recently entered the embedded world, hater of buzzwords and made up titles
I wrote this too fast without even thinking too much, so once again, I appreciate your comment. Anyway, I know that inlining helps, but speed wasn't on my priority list for this challenge. :)
Here are my results:
Your code: ~550 nsec
My code: ~1140 nsec
Thumbs up for faster code.
P.S.
I suspected that strlen might be the culprit, and I was right.
If I change the code like this, I get results similar to yours, around 500 nsec more/less
staticboolcount_xo(constchar*d,size_tl){// null pointerif(!d)returntrue;// no dataif(!l)returntrue;// res countersunsignedxc=0,oc=0;// mid pointunsignedmp=l/2,rm=l%2;// check for 88 (X) and 79 (O)// O(N/2)for(unsignedi=0,j=l-1;i<mp;i++,j--){// X counterxc+=check_x(d[i])+check_x(d[j]);// O counteroc+=check_o(d[i])+check_o(d[j]);}// remainderif(rm){// X counterxc+=check_x(d[mp+1]);// O counteroc+=check_o(d[mp+1]);}// resreturnxc==oc;}
Gentoo Linux and VIM worshiper, C developer, network protocol dissector implementer,socket/network programmer, recently entered the embedded world, hater of buzzwords and made up titles
Gentoo Linux and VIM worshiper, C developer, network protocol dissector implementer,socket/network programmer, recently entered the embedded world, hater of buzzwords and made up titles
The loop tests strings from both sides to be more performant. Try doing benchmarks of both versions and post the results. I don't have time right now but might do it in the evening and show you the difference. Or, I might just embarrass myself lol.
Coding since 11yo, that makes it over 30 years now ~~~
Have a PhD in Comp Sci ~~~
Love to go on bike tours ~~~
I try to stay as generalist as I can in this crazy wide place coding is at now.
Coding since 11yo, that makes it over 30 years now ~~~
Have a PhD in Comp Sci ~~~
Love to go on bike tours ~~~
I try to stay as generalist as I can in this crazy wide place coding is at now.
It's working for me in chrome just now. Try wrapping the output line in a console.log if you're outside a REPL
In the reduce acc is the accumulator, keeping a sum of values as they come up. The reduce returns the sum at the end.
Each c is a character from the string (...str is an array of the chars from str) c == 'x' || c == 'X' is obv a boolean. The code uses the fact that in JS Number(true) == 1 and Number(false) == 0, so it's a shorter way of saying:
Well done! I like a one-liner and clever answers as much as the next person HOWEVER I also love seeing elegant, more readable, and even sometimes simplified (broken down more step by step) solutions!
Very nice! I love your choice of method name 😂 And it's great that you went back to refactor once you realized there were optimizations you could make. No matter what "stakeholders" say about features driving revenue, any developer worth their salt knows that a clean codebase drives features. 👍👍🙌
It basically works by lowercasing everything and building an array with the number of Xs and Os. After that, it builds a Set with it, and check if the Set size is 1 or 2!
19 yo student *and* a full-time developer.
Building with Rails currently, exploring new frontiers.
I enjoy functional programming, Linux, FOSS, refactoring and mentoring :)
I was removing elements with plans of creating two for loops going in opposite directions to speed up the process... but that was for fun and it lost interest in it.
constexsAndOhs=// "🎙 they haunt me…" (https://www.youtube.com/watch?v=0uLI6BnVh6w)(string)=>string.toLowerCase().split("").reduce((count,letter)=>/x/.test(letter)?count+=1:/o/.test(letter)?count-=1:count,0)===0;exsAndOhs("xoxo")// => trueexsAndOhs("like ghosts, they want me")// => falseexsAndOhs("to make them all, they won't let go")// => falseexsAndOhs("ex's and ohs")// => true
TERNARY CHAINS?? Yeah. I'm a loner, Dottie. A rebel…
Now in elixir because hey all the cool kids are using not-Javascript here.
defmoduleExsAndOhsdodeftry_to_get_over_them(string)dostring|>String.downcase|>test(0)# The `Enum` module's for chumps.enddefptest("",count),do:count==0# whole string is traverseddefptest("x"<>string,count),do:# next grapheme is an "x"test(string,count+1)defptest("o"<>string,count),do:# next grapheme is an "o"test(string,count-1)defptest(string,count),do:# String.next_grapheme/1 is a tuple 😃string|>String.next_grapheme|>elem(1)|>test(count)end
This method uses a for loop to go through each character. It converts each character to lower case for a case insensitive comparison with "x" and "o" and increments the respective counters. The return line checks for count equality to return a boolean.
The time complexity in big O notation is O(n) or linear, where n is the size of input string s, and the space complexity is O(1) or constant since it just uses two variables to keep track of counts.
This can be done in linear time and linear space in terms of complexity by keeping track of the number of letter in a Map object as you traverse the string. Do note that the letter should be case insensitive which that actually gives us a hint that we can make each of the character to be always lowercased before using it as a cache key.
Pseudocode:
a = 'xoxo'
m = Map()
loop for each n in a {
l = n.toLowerCase()
m.set(l, 1) if not m.has(l)
else m.set(l, 1 + m.get(l))
}
functionXO(str){letstr_arr=str.toLowerCase().replace(/[^xo]/g,'').split('')//Convert String into an Array to allow using Array.sort().sort();//All of the o's will be placed before the x's thanks to Array.sort()//Add 1 to account for 0-indexing and to turn -1 into 0 when there are no o'sletoCount=str_arr.lastIndexOf('o')+1;letxCount=str_arr.length-oCount;return!(xCount-oCount);}//Run the test casesconsttestCases=["ooxx","xooxx","ooxXm","zpzpzpp","zzoo"];for(stroftestCases){letresult=XO(str);console.log(`"${str}" => ${result}`);}
JavaScript
A solution using regular expressions in JavaScript. Live demo on CodePen.
Oh look, you had already come up with the same idea as I did!
Yes. Although mine looks a bit uglier because I ran into an issue: if the string doesn't have Xs or Os, the result of
matchis null so it fails when trying to do.length(testcheckXO("zpzpzpp")on your code).I had to add the fallback into empty array for those cases. But I feel like there has to be a cleaner way of doing the same thing.
Yeah, I found the same issue when I tried to solve it in Codewars. So I had to do the same. Looking through other people's solutions, there's one that I find interesting that uses
split()instead of a regular expression. Something like this:The split approach is interesting. I was playing with different options, but not with this. Also, I found that using
toLowerCase()made the code considerably slower, it works considerably faster if you use a regular expression in the split:I think this is my favourite solution. I hadn't checked its speed, but I thought that
toLowerCase()probably didn't make it any smoother than returning an empty array if there are no matches.Haskell again
dammit kevin
Just being able to use Haskell impresses me lol
I can't do anything useful with it yet, but it's fun to solve coding katas :D.
Very easy (and rather fast) in Perl:
It uses the transliteration operator which returns the number of matching characters in scalar context, and numeric comparison imposes scalar context.
The tests:
Perl sorcery 😁. This syntax always turns me inside out hehe. Nice job 👍
Sure, but this one is pretty easy to demystify, luckily:
tr/set1/set2/just replaces characters inset1with those inset2(positionally), and returns the number of characters replaced/deleted. So in this code, he just compares the number of X's replaced with the number of O's replaced, and includes the lower-case variants too.The
local($_)is just there to let him make the code more brief.$_is the "default input/pattern-searching space", so its like the default argument thattrwill search. But it's also a global, so this lets you make a local copy and set it to be the first argument to the function.If he didn't do that, it might look like this:
So just a bit more verbose.
Thanks for a great explanation :)
C 😱 don't kill me please
Results:
Is it allowed to modify someone else’s solution? I think you made it a bit more complicated than necessary. Anyone please correct me if I’m wrong:
C strings end with the null character
'\0', so you can skip getting the string length and do a while loop that checks for the null character (strlendoes just that anyway);I didn’t really understand why you’re walking the string from the start and the middle, instead of from beginning to end, so I changed it;
since we then only test for
xandoonce, I could inline the tests;xc == ocandd == 88 || d == 120are already boolean expressions, so you don’t need the ternary operator... ? true : falseor anif(...) return true; else return false;;since I never used assertions in C, I took the opportunity to try them for the test cases (I intentionally wrote
assert(count_xo("xooxx") == false)in the tests instead ofassert(! count_xo("xooxx"))for consistency and readability.I wrote this too fast without even thinking too much, so once again, I appreciate your comment. Anyway, I know that inlining helps, but speed wasn't on my priority list for this challenge. :)
Here are my results:
Your code: ~550 nsec
My code: ~1140 nsec
Thumbs up for faster code.
P.S.
I suspected that
strlenmight be the culprit, and I was right.If I change the code like this, I get results similar to yours, around 500 nsec more/less
Nice. I didn’t inline for speed, but for compactness, so that people don’t say C is too complicated, so verbose, etc. :-)
C is my baby, just started embedded C on SBCs, mostly arm 32bit. And people will always say that 🤣👍
Hi and no problem changing the code
The following is indeed not needed:
The loop tests strings from both sides to be more performant. Try doing benchmarks of both versions and post the results. I don't have time right now but might do it in the evening and show you the difference. Or, I might just embarrass myself lol.
Cheers
How about a reduce to keep it in one loop? (JS)
Oh, nice! I wouldn't have thought to use the array spread syntax to get the array of characters. 🎩✨
Bro, nice solution .cud u explain me what is going inside the reduce. And im not able to reproduce it.
It's working for me in chrome just now. Try wrapping the output line in a
console.logif you're outside a REPLIn the reduce
accis the accumulator, keeping a sum of values as they come up. The reduce returns the sum at the end.Each
cis a character from the string (...stris an array of the chars fromstr)c == 'x' || c == 'X'is obv a boolean. The code uses the fact that in JSNumber(true) == 1andNumber(false) == 0, so it's a shorter way of saying:So each x adds one to the sum, and each o subtracts one. If the sum is zero there are the same number of x's as o's
Cool.its working😃
Javascript (pretty readable):
Well done! I like a one-liner and clever answers as much as the next person HOWEVER I also love seeing elegant, more readable, and even sometimes simplified (broken down more step by step) solutions!
Awesome job!
Thank you Jacob! I have the same opinion! 👍
Not a pretty solution, but it seems to work in Ruby
Returns
WAIT. Here's a much cleaner solution in Ruby.
Which returns
This is why you refactor!
It turns out that you could avoid the
#downcasewithstring.count("xX") == string.count("oO")I don't know whether the performance would be significantly different, though it would avoid creating a couple of extra string instances.
There are some other interesting variations for
#count, such as using it to count lower case letters with"abGvf".count("a-z")Very nice! I love your choice of method name 😂 And it's great that you went back to refactor once you realized there were optimizations you could make. No matter what "stakeholders" say about features driving revenue, any developer worth their salt knows that a clean codebase drives features. 👍👍🙌
Thanks. I can't think of X's and O's without Gossip Girl.
Clean codebases are so much easier to work with. Not to mention, cost and performance savings.
How about some ugly oneliner?
Which produces the following output:
It basically works by lowercasing everything and building an array with the number of Xs and Os. After that, it builds a Set with it, and check if the Set size is 1 or 2!
This is my solution, I'm still learning javascript so nothing fancy
Elixir:
I broke out the work a little more, I tried to be a tad clever and utilize switch but wasn't able to get it to work quite the way I wanted.
Hopefully, the slightly more vanilla JS will be useful in some way.
I was removing elements with plans of creating two for loops going in opposite directions to speed up the process... but that was for fun and it lost interest in it.
Ruby
Shorter solutions, but this only loops through the string once which might be nice for enormous strings.
Hmmm... Something like this might work in JavaScript
Rust
Rust Playground
GitHub Gist
TERNARY CHAINS?? Yeah. I'm a loner, Dottie. A rebel…
Now in elixir because hey all the cool kids are using not-Javascript here.
Haskell:
A little Python here.
This method uses a for loop to go through each character. It converts each character to lower case for a case insensitive comparison with "x" and "o" and increments the respective counters. The return line checks for count equality to return a boolean.
The time complexity in big O notation is
O(n)or linear, wherenis the size of input strings, and the space complexity isO(1)or constant since it just uses two variables to keep track of counts.Is there a reason why you prefer a for loop over a single call to lower on the str, and then returning lowered.count('x') == lowered.count('o') ?
This can be done in linear time and linear space in terms of complexity by keeping track of the number of letter in a Map object as you traverse the string. Do note that the letter should be case insensitive which that actually gives us a hint that we can make each of the character to be always lowercased before using it as a cache key.
Pseudocode:
a = 'xoxo'
m = Map()
loop for each n in a {
l = n.toLowerCase()
m.set(l, 1) if not m.has(l)
else m.set(l, 1 + m.get(l))
}
return True if not m.has('x') and not m.has('o')
return m.get('x') == m.get('o');
Javascript with no loops:
Python goes there :
[V2] - One liner :
My solution in js
Python: