Giter VIP home page Giter VIP logo

codewars's People

Contributors

soonlive avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

temilolu

codewars's Issues

Decorator Pattern

Decorator Pattern

Description:

The Decorator Pattern is a design pattern that allows behaviour to be added to an existing object/function dynamically without affecting its implementation.

This pattern is designed so that multiple decorators can be stacked on top of each other, each time adding a new functionality to the overridden function(s).

The decorator pattern is an alternative to subclassing. Subclassing adds behavior at compile time, and the change affects all instances of the original class; decorating can provide new behavior at run-time for individual objects/functions.

Motivation:

We will explain the utility of the decorator pattern with a simple example.

Suppose we have the sum() function:

//returns the sum of the arguments
function sum() {
  return Array.prototype.reduce.call(arguments, function(sum, value) {
    return sum + value;
  }, 0);
}

sum(1, 4, 8, 9) //22

Now suppose we want to sum only the numbers are between 1 and 9. An intrusive and non-reusable solution could be:

//returns the sum of the arguments between 1 and 9
function sum() {
  return Array.prototype.reduce.call(arguments, function(sum, value) {
    if (value >= 1 && value <= 9) {
      return sum + value;
    } else {
      return sum;
    }
  }, 0);
}

sum(1, 9, -3, 1, 0, 4, 8, 9, 12); // 22

Although this solution is correct, it is not reusable because it is difficult to modify the minimum and maximum limits or add new functionality.

A better solution would be to define the filter(min, max) function:

//returns an array containing the numbers that are between min and max
//the numbers are passed from third position
function filter(min, max) {
  return Array.prototype.slice.call(arguments, 2).filter(function(value) {
    return value >= min && value <= max;
  });
}

filter(1, 9, -3, 1, 0, 4, 8, 9, 12); // [1, 4, 8, 9]

Now we can call the sum() function like this:

//returns the sum of the arguments between 1 and 9
sum.apply(null, filter(1, 9, -3, 1, 0, 4, 8, 9, 12)); //22

This looks great. We have now achieved the same result without affecting the sum() function and we have an independent function, filter(min, max), we can apply to other functions in addition to the sum() function.

For example:

//returns the multiplication of the arguments
function multiplication() {
  return Array.prototype.reduce.call(arguments, function(product, value) {
    return product * value;
  }, 1);
}

multiplication.apply(null, filter(1, 9, -3, 1, 0, 4, 8, 9, 12));  //288

Now suppose we define the noNumbersFilter() function that removes those values that can not be added because they are not numeric:

function filterNoNumbers() {
  return Array.prototype.filter.call(arguments, function(value) {
    return typeof value === 'number' && value === value && value !== Number.POSITIVE_INFINITY && value !== Number.NEGATIVE_INFINITY;
  });
}

filterNoNumbers(-3, NaN, 1, 0, "2", 4, 8, 9, 12); // [-3, 1, 0, 4, 8, 9, 12]

We could do the same and apply the filterNoNumbers() function in this way:

sum.apply(null, filter.apply(null, [1, 9].concat(filterNoNumbers(-3, NaN, 1, 0, "2", 4, 8, 9, 12)))); // 22
Even if it works, it starts to be a challenge to do this without errors.

We can further complicate the problem if we want the result of the sum has to be rounded to two decimal digits. To do this, we define the round(decimals) function:

function round(decimals) {
  if (arguments.length === 2) {
    return arguments[1].toFixed(decimals);
  } else {
    return Array.prototype.splice.call(arguments, 1).map(function(value) {
      return value.toFixed(decimals);
    });
  }
}

round(2, 1, 2.1, 2.346); // ["1.00", "2.10", "2.35"]
round(2, 1.3); // "1.30"

And we use it in this way:

round.apply(null, [2].concat(sum.apply(null, filter.apply(null, [1, 9].concat(filterNoNumbers(-3, 1.016, 0, 4, NaN, 8.041, '27', 9, 12)))))); // "22.06"

It would be better if we could generalize the solution. This is where the decorator pattern comes into play.

Work to do:

Your work is implement the decorate(fn) function. Note that this function takes the function to decorate. The decorate(fn) function can also receive the parameters passed to the decorator functions. See the example below.

When you call the decorate(fn) function, a decorated function will be returned. When this returned function is called, the function fn() passed in decorate(fn) will be called applying decorator functions.

The Decorator(options) function is already implemented. Note that the function receives an object with two attributes before and after. These attributes are the decorator functions which apply before and after calling the function passed in decorate(fn), respectively. One or both may be null.

The way to use these functions are as follows:

var filterDecorator = new Decorator({
  before : filter
});

var filterNoNumbersDecorator = new Decorator({
  before : filterNoNumbers
});

var roundDecorator = new Decorator({
  after: round
});

var decoratedSum = filterDecorator.decorate(sum, 1, 9); // 1 and 9 are the min and max parameters passed to the filter function
decoratedSum = filterNoNumbersDecorator.decorate(decoratedSum);
decoratedSum = roundDecorator.decorate(decoratedSum, 2); // rounded to two decimals

decoratedSum(-3, 1.016, 0, 4, NaN, 8.041, '27', 9, 12); // "22.06"

题意:

作者用了大篇幅为我们诠释装饰者模式,要求就是定义一个名为 Decorator 的 function,实现该模式。

思路:

我想到了 java 的 AOP 切面编程

Solution:

  function Decorator(options) {
    if (!options) {
      options = {};
    }
    this.before = options.before;
    this.after = options.after;
  }

  Decorator.prototype.decorate = function decorate(fn) {
    if (!this.before && !this.after) {
      return typeof fn === 'function' ? fn : fn.apply(null, arguments);
    }

    var _this = this;
    var args = [].slice.call(arguments).filter(function (a) {
      return typeof a !== 'function';
    });

    return function () {
      var result;

      var innerArgs = [].slice.call(arguments);
      if (_this.before) {
        result = fn.apply(null, _this.before && _this.before.apply(null, args.concat(innerArgs)));
      }

      if (_this.after) {
        result = _this.after && _this.after.apply(null, args.concat(fn.apply(null, _this.before ? result : innerArgs)));
      }

      return result
    };
  };

Find heavy ball - level: master

Find heavy ball - level: master

Description:

There are 8 balls numbered from 0 to 7. Seven of them have the same weight. One is heavier. Your task is to find it's number.

Your function findBall will receive single argument - scales object. The scales object contains an internally stored array of 8 elements (indexes 0-7), each having the same value except one, which is greater. It also has a public method named getWeight(left, right) which takes two arrays of indexes and returns -1, 0, or 1 based on the accumulation of the values found at the indexes passed are heavier, equal, or lighter.

getWeight returns:

-1 if left pan is heavier

1 if right pan is heavier

0 if both pans weigh the same

Examples of scales.getWeight() usage:

scales.getWeight([3], [7]) returns -1 if ball 3 is heavier than ball 7, 1 if ball 7 is heavier, or 0 i these balls have the same weight.

scales.getWeight([3, 4], [5, 2]) returns -1 if weight of balls 3 and 4 is heavier than weight of balls 5 and 2 etc.

So where's the catch, you may ask. Well - the scales is very old. You can use it only TWICE before the scale breaks.

题意:

8 个球,7 个的重量是相同的,另一个重量比较重,用天平称两次,找出最重的球

思路:

先拿 6个左右各 3个称一次,根据称的结果,再拿 2 个左右各一个称一次,即可得出

  function findBall(scales) {
    var idx = -1;
    var w = scales.getWeight([0, 1, 2], [3, 4, 5]);
    if (w > 0) {
      w = scales.getWeight([3], [4]);
      if (w > 0) {
        idx = 4
      } else if (w < 0) {
        idx = 3
      } else {
        idx = 5
      }
    } else if (w < 0) {
      w = scales.getWeight([0], [1]);
      if (w > 0) {
        idx = 1
      } else if (w < 0) {
        idx = 0
      } else {
        idx = 2
      }
    } else {
      w = scales.getWeight([6], [7]);
      idx = w > 0 ? 7 : 6;
    }

    return idx;
  }

Can you get the loop ?

Can you get the loop ?

Description:

You are given a node that is the beginning of a linked list. This list always contains a tail and a loop.

Your objective is to determine the length of the loop.

For example in the following picture the tail's size is 3 and the loop size is 11.

image

// Use the `getNext' method or 'next' property to get the following node.

node.getNext()
node.next

题意:

找出循环链表的长度

思路:

找出循环开始的第一个 node,就可以计算出循环链表的长度
image

Solution:

  function loop_size(node) {
    var loopArr = [];
    while (node && loopArr.indexOf(node) < 0) {
      loopArr.push(node);
      node = node.next;
    }
    var firstIndex = Math.max(0, loopArr.indexOf(node));
    return loopArr.length - firstIndex;
  }

ROT13

ROT13

Description:

How can you tell an extrovert from an introvert at NSA? Va gur ryringbef, gur rkgebireg ybbxf ng gur BGURE thl'f fubrf.

I found this joke on USENET, but the punchline is scrambled. Maybe you can decipher it? According to Wikipedia, ROT13 (http://en.wikipedia.org/wiki/ROT13) is frequently used to obfuscate jokes on USENET.

Hint: For this task you're only supposed to substitue characters. Not spaces, punctuation, numbers etc. Test examples:

Test.expect(rot13("EBG13 rknzcyr.") == "ROT13 example.");
Test.expect(rot13("This is my first ROT13 excercise!") == "Guvf vf zl svefg EBG13 rkprepvfr!")

题意:

将字符串中的字母替换为在26个字母表中向右移动13个位置后的字母,区分大小写

Solution:

function rot13(str) {
  return str.split('').map(function (n) {
      if(/[A-Za-z]/.test(n)){
          var charCode = n.charCodeAt(0);
          n = String.fromCharCode(charCode>109 || (charCode>77 && charCode<91)?charCode-13:charCode+13);
      }
      return n;
  }).join('');
}

Merged String Checker

Merged String Checker

Description:

At a job interview, you are challenged to write an algorithm to check if a given string, s, can be formed from two other strings, part1 and part2.

The restriction is that the characters in part1 and part2 are in the same order as in s.

The interviewer gives you the following example and tells you to figure out the rest from the given test cases.

For example:

'codewars' is a merge from 'cdw' and 'oears':

    s:  c o d e w a r s   = codewars
part1:  c   d   w         = cdw
part2:    o   e   a r s   = oears

题意:

判断字符 s 是否由另外两个字符 part1、part2 合并而成,要考虑合并字符的顺序是否正确

Solution:

  function isMerge(s, part1, part2) {
    var arr = new Array(s.length);
    for(var i =0;i< s.length;++i){
      if(part1.indexOf(s[i]) > -1 &&  arr[i] === null){
        arr[i] = s[i];
      }
      if(part2.indexOf(s[i]) > -1 &&  arr[i] === null){
        arr[i] = s[i];
      }
    }
    return s.length === (part1.length + part2.length) && isOrderCorrect(s,part1) && isOrderCorrect(s,part2) && arr.join('') === s;
  }


  function isOrderCorrect(s,part){
    var o=-1;
    return part.split('').every(function(c) {
      return (o=s.indexOf(c,o+1)) !== -1;
    });
  }

Palindrome chain length

Palindrome chain length

Description:

Number is a palindrome if it is equal to the number with digits in reversed order. For example, 5, 44, 171, 4884 are palindromes and 43, 194, 4773 are not palindromes.

Write a method palindrome_chain_length which takes a positive number and returns the number of special steps needed to obtain a palindrome. The special step is: "reverse the digits, and add to the original number". If the resulting number is not a palindrome, repeat the procedure with the sum until the resulting number is a palindrome.

If the input number is already a palindrome, the number of steps is 0.

Input will always be a positive integer.

For example, start with 87:

87 + 78 = 165; 165 + 561 = 726; 726 + 627 = 1353; 1353 + 3531 = 4884

4884 is a palindrome and we needed 4 steps to obtain it, so palindrome_chain_length(87) == 4

题意:

给定一个数字,求需要经过多少次与自身的镜像数字相加才能变成回文

Solution:

var palindromeChainLength = function (n) {
    var count = 0;
    var palindrome = parseInt((n + '').split('').reverse().join(''));
    while (palindrome !== n) {
      n = parseInt(n) + parseInt(palindrome);
      palindrome = parseInt((n + '').split('').reverse().join(''));
      ++count;
    }

    return count;
  };

Next bigger number with the same digits

Next bigger number with the same digits

Description:

You have to create a function that takes a positive integer number and returns the next bigger number formed by the same digits:

nextBigger(12)==21
nextBigger(513)==531
nextBigger(2017)==2071

If no bigger number can be composed using those digits, return -1:

nextBigger(9)==-1
nextBigger(111)==-1
nextBigger(531)==-1

题意:

给定一个正整数 n, 找出下一个由组成 n 的数字组成的比 n 大的数(有点绕口。。)

思路:

高位的数是相同的,关键点在于找到 n[i] < n[i+1],然后保持左侧高位数字不变,将右侧数字按从小到大进行排序,找到第一个比 n[i] 大的数字,然后与 n[i] 进行调换,即得到下一个比 n 大的数字。

image

Solution:

  /**
   * 
   * @param {number} n
   * @returns {number}
   */
  function nextBigger(n) {
    var nextBigNum = -1;
    var nums = n.toString().split('');
    for (var i = nums.length - 1; i >= 0; --i) {
      if (nums[i] < nums[i + 1]) {
        var rightPartNums = nums.splice(i + 1).sort();
        for (var j = 0; j < rightPartNums.length; ++j) {
          if (rightPartNums[j] > nums[i]) {
            nums[i] = rightPartNums[j] - nums[i];
            rightPartNums[j] = rightPartNums[j] - nums[i];
            nums[i] = rightPartNums[j] + nums[i];
            nextBigNum = parseInt(nums.concat(rightPartNums).join(''), 10);
            i = 0;
            break;
          }
        }
      }
    }

    return nextBigNum;
  }

(Ready for) Prime Time

(Ready for) Prime Time

Description:

We need prime numbers and we need them now!

Write a method that takes a maximum bound and returns all primes starting with 0 up-to and including the maximum bound.

For example:

prime(11);

Should return an array that looks like this:

[2,3,5,7,11]

题意:

给定一个 n,返回包含 0 ~ n 的所有质数

思路:

这道题脑洞开的有点大,用 n 个 ‘0’表示n
00 2
000 3
0000 4
00000 5
000000 6
0000000 7
00000000 8
000000000 9
0000000000 10
00000000000 11

能平均分组的都不是质数,用正则进行匹配
00 2
0(00) 3
(00)(00) 4
0(00)(00) 5
(000)(000) 6
0(000)(000) 7
(0000)(0000) 8
(000)(000)(000) 9
(00000)(00000) 10
0(00000)(00000) 11

Solution:

  function prime(num) {
    var arr = [];
    while (num >= 2) {
      if (!/^(11+?)\1+$/g.test('0'.repeat(num))) {
        arr.unshift(num);
      }
      --num;
    }
    return arr;
  }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.