Ad
Code
Diff
  • rps=(a,b)=>a==b?'Draw!':`Player ${2-/sp|rr|ks/.test(a+b)} won!`
    • const rps=(p,z)=>p==z?'Draw!':`Player ${1+/sr|ps|rp/.test(p[0]+z[0])} won!`;
    • rps=(a,b)=>a==b?'Draw!':`Player ${2-/sp|rr|ks/.test(a+b)} won!`

Whether you're a kata solver or a kata author, Codewars allows you to embed client-side
HTML, CSS, and JavaScript in your outputs.

This goes well beyond ASCII art. The door is open to more sophisticated debugging and visualization techniques. For a contrived and sloppily coded example, run this kata; in the output panel there should be a snake which, if luck will have it, you can control via the arrow keys on your keyboard.

Some JavaScript methods are disabled in this environment, but even so, for kata authors with JavaScript+CSS experience and full DOM control, the sky's the limit.

// The client-side stuff that this demo is made of is
// in the preloaded section, but it's not pretty or necessary
// to look at so I'm leaving it out of the main code view.
// The purpose of this kumite isn't to demonstrate *how* to embed HTML.
// That's as trivial as console.log(). The point, rather, is
// to make a public service anouncement to remind everyone
// that HTML, CSS, and JavaScript are available, and when
// deployed appropriately they can improve your katas.
Logic
Arrays
Data Types
Objects

Here, I use a lookup table and map over the order array instead of sorting.

Advantages

  • Unlike sort, this won't mutate the original array
  • This should be significantly faster for large arrays

Limitations

  • Keys must be literal values or - in the limited and error prone case - be flat arrays of literal values
Code
Diff
  • function mapOrder(array, order, key) {
      if (!Array.isArray(array)) return [];
      
      const lookupTable = array.reduceRight(
        (table, o) => {
          table[o[key]] = o;
          return table;
        },
        {}
      );
      
      return order.map(k => lookupTable[k]);
    }
    • function mapOrder(array, order, key) {
    • if (typeof array !== 'object' || typeof order !== 'object' || typeof key !== 'string') return [];
    • if (!Array.isArray(array)) return [];
    • array.sort((a, b) => {
    • let A = a[key], B = b[key];
    • if (order.indexOf(A) > order.indexOf(B)) {
    • return 1;
    • } else return -1;
    • });
    • return array;
    • const lookupTable = array.reduceRight(
    • (table, o) => {
    • table[o[key]] = o;
    • return table;
    • },
    • {}
    • );
    • return order.map(k => lookupTable[k]);
    • }
Fundamentals
Code
Diff
  • e=lambda n:n-n%2
    • e=lambda n:n//2*2
    • e=lambda n:n-n%2

Same thing in Prolog, with results rounded to the nearest hundredth.

Code
Diff
  • final_price(Price, Result) :-
      Vat = 0.1, Lux = 0.3,
      ( Price >= 1000 -> Unrounded is Price*(1+Vat+Lux)
      ; Unrounded is Price*(1+Vat)
      ),
      Result is round(Unrounded*100)/100.
    • function finalPrice(price) {
    • var vat = 0.1
    • var lux = 0.3
    • if(price >= 1000) {
    • return Math.floor(price * (1+vat+lux))
    • } else {
    • return Math.floor(price * (1+vat))
    • }
    • }
    • final_price(Price, Result) :-
    • Vat = 0.1, Lux = 0.3,
    • ( Price >= 1000 -> Unrounded is Price*(1+Vat+Lux)
    • ; Unrounded is Price*(1+Vat)
    • ),
    • Result is round(Unrounded*100)/100.

toFixed can be used on any number directly, thus making parseFloat superfluous.

Code
Diff
  • function finalPrice(price) {
      const vat = 0.1,
            lux = 0.3,
            luxThreshold = 1000;
      
      const multiplier = price >= luxThreshold ? 1+vat+lux : 1+vat;
      return (price * multiplier).toFixed(2);
    }
    • function finalPrice(price) {
    • const vat = 0.1;
    • const lux = 0.3;
    • const luxThreshold = 1000;
    • const vat = 0.1,
    • lux = 0.3,
    • luxThreshold = 1000;
    • const extra = price >= luxThreshold ? lux : 0;
    • return Number.parseFloat(price * (1 + vat + extra)).toFixed(2);
    • const multiplier = price >= luxThreshold ? 1+vat+lux : 1+vat;
    • return (price * multiplier).toFixed(2);
    • }

Not an improvement in any objective sense, but here's another way of doing it...

Code
Diff
  • function getTree(strings, root={}) {
      for (let path of strings) {
        let tree = root, parts = path.split("/");
        for (let part of parts.slice(0,-1))
          tree = tree[part] = tree[part] || {};
        tree[parts.pop()] = "file";
      }
      return root;
    }
    • function set(obj, path, value) {
    • if(path.length === 1) {
    • return {...obj, [path[0]]: value};
    • }
    • const first = path.shift();
    • return {...obj, [first]: set(obj[first] || {}, path, value)};
    • }
    • function getTree(strings) {
    • return strings.reduce(function r(tree, curr) {
    • return set(tree, curr.split('/'), "file");
    • }, {});
    • function getTree(strings, root={}) {
    • for (let path of strings) {
    • let tree = root, parts = path.split("/");
    • for (let part of parts.slice(0,-1))
    • tree = tree[part] = tree[part] || {};
    • tree[parts.pop()] = "file";
    • }
    • return root;
    • }
Code
Diff
  • function getTree(strings) {
      return strings.reduce(
        (root, path) => {
          let items = path.split("/").filter(x=>x);
          if (!items.length) return root;
          
          let branch = root;
          while (items.length > 1) {
            let item = items.shift();
            branch=branch[item]=branch[item]||{};
          }
          branch[items[0]] = "file"; 
          
          return root;
        },
        {}
      );
    }
    • function set(obj, path, value) {
    • if(path.length === 1) {
    • return {...obj, [path[0]]: value};
    • }
    • const first = path.shift();
    • return {...obj, [first]: set(obj[first] || {}, path, value)};
    • }
    • function getTree(strings) {
    • return strings.reduce(function r(tree, curr) {
    • return set(tree, curr.split('/'), "file");
    • }, {});
    • return strings.reduce(
    • (root, path) => {
    • let items = path.split("/").filter(x=>x);
    • if (!items.length) return root;
    • let branch = root;
    • while (items.length > 1) {
    • let item = items.shift();
    • branch=branch[item]=branch[item]||{};
    • }
    • branch[items[0]] = "file";
    • return root;
    • },
    • {}
    • );
    • }

Here's the golf edition nobody asked for.

Code
Diff
  • finalPrice=p=>p*(p>=1e3?1.4:1.1)|0
    • function finalPrice(price) {
    • var vat = 0.1
    • var lux = 0.3
    • if(price >= 1000) {
    • return Math.floor(price * (1+vat+lux))
    • } else {
    • return Math.floor(price * (1+vat))
    • }
    • }
    • finalPrice=p=>p*(p>=1e3?1.4:1.1)|0
Fundamentals
Algorithms
Logic

In my opinion, the code is cleaner with destructuring and math.hypot. Also, snake_case is preferred.

Code
Diff
  • import math
    
    def goes_to_jail(directions):
        x = 0 ; y = 0
        for north,east,south,west in directions:
            x += (east - west) * 274
            y += (north - south) * 80
            if math.hypot(x, y) > 2025:
                return True
        return False
    • def goesToJail(directions):
    • location=[0,0]
    • #North+East are postive numbers West+South are a negative numbers
    • for direction in directions:
    • location[0]=location[0]+(direction[0]*80)-(direction[2]*80)
    • location[1]=location[1]+(direction[1]*274)-(direction[3]*274)
    • #a squared + b squared = c squared
    • if( (location[0]**2+location[1]**2)**(1/2) > 2000 ):
    • import math
    • def goes_to_jail(directions):
    • x = 0 ; y = 0
    • for north,east,south,west in directions:
    • x += (east - west) * 274
    • y += (north - south) * 80
    • if math.hypot(x, y) > 2025:
    • return True
    • return False

Golfed solution.

Code
Diff
  • function* generateInterval(a,b,c){for(;c>0?a<=b:a>=b;a+=c)yield a}
    • function* generateInterval(start, stop, step) {
    • if (step >= 0) { // infinite lists with step 0 are a-okay
    • for (let cursor=start; cursor<=stop; cursor+=step) yield cursor;
    • } else if (step < 0) {
    • for (let cursor=start; cursor>=stop; cursor+=step) yield cursor;
    • }
    • }
    • function* generateInterval(a,b,c){for(;c>0?a<=b:a>=b;a+=c)yield a}

Using a generator and two for loops for clarity.

Code
Diff
  • function* generateInterval(start, stop, step) {
      if (step >= 0) { // infinite lists with step 0 are a-okay
        for (let cursor=start; cursor<=stop; cursor+=step) yield cursor;
      } else if (step < 0) {
        for (let cursor=start; cursor>=stop; cursor+=step) yield cursor;
      }
    }
    • function generateInterval(start, stop, step) {
    • const arr = [];
    • let value = start;
    • for (let index = 0; index <= (stop - start) / step; index++) {
    • arr.push(value);
    • value += step;
    • function* generateInterval(start, stop, step) {
    • if (step >= 0) { // infinite lists with step 0 are a-okay
    • for (let cursor=start; cursor<=stop; cursor+=step) yield cursor;
    • } else if (step < 0) {
    • for (let cursor=start; cursor>=stop; cursor+=step) yield cursor;
    • }
    • return arr;
    • }

I'm using capturing split instead of match. This handles a couple of edge cases including empty string inputs and inputs containing two numbers in a row.

Code
Diff
  • function transformText(s) {
      return s.split(/(\d+)/)
        .map(w => w.trim())
        .filter(w => w)
        .map(w => (
          /\d/.test(w) ? `<div>${w}</div>` : `<span>${w}</span>`
        ))
        .join``;
    }
    • function transformText(string) {
    • const regExp = /[\D]+|([\d+\.]+)/g
    • const numsRegExp = /[0-9\.]+/g
    • const splited = string.match(regExp)
    • const res = splited.map((item) => {
    • if (numsRegExp.test(item)) {
    • return `<div>${item}</div>`
    • } else {
    • return `<span>${item.replace(/^\s|\s$/g, "")}</span>`
    • }
    • })
    • return res.join("")
    • function transformText(s) {
    • return s.split(/(\d+)/)
    • .map(w => w.trim())
    • .filter(w => w)
    • .map(w => (
    • /\d/.test(w) ? `<div>${w}</div>` : `<span>${w}</span>`
    • ))
    • .join``;
    • }