String Mix

链接

CodeWar-String Mix

题目

给出两个字符串s1和s2,我们想要可视化两个字符串的差别。我们只计算小写字母(a到z)。首先让我们计算s1和s2中每个小写字母出现的频率。

s1 = "A aaaa bb c"

s2 = "& aaa bbb c d"

s1 has 4 'a', 2 'b', 1 'c'

s2 has 3 'a', 3 'b', 1 'c', 1 'd'

所以s1和s2中’a’的最大值是来自s1的4;'b’的最大值是来自s2的3。接下来我们不考虑最高出现频率小于或者等于1的字母。

我们可以通过下面这个字符串来简要概括s1和s2之间的差别:“1:aaaa/2:bbb”。其中1:aaaa1代表字符串s1,aaaa是因为a的最大值为4。同理,2:bbb代表字符串s2,bbb是由于b的最大值是3。

任务是产生一个字符串使得s1和s2中每一个小写字母如果出现的最大值大于1,则在字符串中出现和最大值相同的次数。这些字母将会由数字作为前缀,并且加上:。如果s1和s2中的最大值相等则前缀是=:

在结果中,子字符串(一个子字符串是例如2:nnnnn或者1:hhh;它包含前缀)将会根据它们的长度降序排列,当它们的长度相同时,按上升词典序排列。不同的分组由’/'分割。详见例子。

希望其他例子能把这个表达清晰一些。

s1 = "my&friend&Paul has heavy hats! &"
s2 = "my friend John has many many friends &"
mix(s1, s2) --> "2:nnnnn/1:aaaa/1:hhh/2:mmm/2:yyy/2:dd/2:ff/2:ii/2:rr/=:ee/=:ss"

s1 = "mmmmm m nnnnn y&friend&Paul has heavy hats! &"
s2 = "my frie n d Joh n has ma n y ma n y frie n ds n&"
mix(s1, s2) --> "1:mmmmmm/=:nnnnnn/1:aaaa/1:hhh/2:yyy/2:dd/2:ff/2:ii/2:rr/=:ee/=:ss"

s1="Are the kids at home? aaaaa fffff"
s2="Yes they are here! aaaaa fffff"
mix(s1, s2) --> "=:aaaaaa/2:eeeee/=:fffff/1:tt/2:rr/=:hh"

我的思路及实现

function mix(s1, s2) {
    var regs = /[^a-z]/g;
    var arr1 = s1.replace(regs, "").split("").sort().join('').match(/([^])\1+/g) || [];
    var arr2 = s2.replace(regs, "").split("").sort().join('').match(/([^])\1+/g) || [];
    
    function sort2(a,b){
        if (a.length > b.length) {
          return -1;
        } else if (a.length < b.length) {
          return 1;
        } else {
          if (b<a) {
            return 1;          
          } else if (b>a) {
            return -1
          }
        }
    }
    
    arr1.sort(sort2);
    arr2.sort(sort2);
    
    var arrNew = [];
    arr1.forEach((element, i) => {
      var str1 = element.slice(0,1);
      var f = true;
      arr2.forEach((ele,i) => {
        var str2 = ele.slice(0,1);
        if (str1 == str2) {
          if (element.length > ele.length) {
            var obj = {
                ele: element,
                index: '1:'
            };
            arrNew.push(obj);
          } else if (element.length == ele.length) {
            var obj = {
                ele: element,
                index: '=:'
            };
            arrNew.push(obj);
          } else if (element.length < ele.length) {
            var obj = {
                ele: ele,
                index: '2:'
            };
            arrNew.push(obj);
          }
          arr2.splice(i, 1);
          f = false;
        }
      })
      if (f) {
        var obj = {
            ele: element,
            index: '1:'
        };
        arrNew.push(obj);
      }
    });
    
    arr2.forEach(ele2 => {
     var obj = {
            ele: ele2,
            index: '2:'
        };
        arrNew.push(obj);
    })
    
    arrNew.sort((a, b) => {
      if (a.ele.length > b.ele.length) {
        return -1;
      } else if (a.ele.length < b.ele.length) {
        return 1;
      } else {
        if (a.index > b.index) {
            return 1;
        } else if (a.index < b.index) {
            return -1;
        } else {
          var t1 = b.ele.slice(0,1);
          var t2 = a.ele.slice(0,1);
          return 0-(t1.charCodeAt(0)-t2.charCodeAt(0));
        }
      }
    })
    
    var result = "";
    arrNew.forEach(ele => {
      result += ele.index + ele.ele + '/';
    })
    
    return result.slice(0, result.length-1);
}

Best Practice

function mix(s1, s2) {
  var counter = s => s.replace(/[^a-z]/g,'').split('').sort().reduce((x,y)=> (x[y] = 1 + (x[y]||0), x),{});
  s1 = counter(s1); s2 = counter(s2);
  var res = [], keys = new Set(Object.keys(s1).concat(Object.keys(s2)));
  keys.forEach(key => {
    var c1 = s1[key]||0, c2 = s2[key]||0, count = Math.max(c1, c2);
    if (count>1) {
      var from = [1, '=', 2][Math.sign(c2-c1)+1];
      var str = [...Array(count)].map(_=>key).join('');
      res.push(from+':'+str);
    }
  });
  return res.sort((x, y) => y.length - x.length || (x < y ? -1 : 1)).join('/');
}
const alphabet = 'abcdefghijklmnopqrstuvwxyz'.split('');

function mix(s1, s2) {
  return alphabet
    .map(char => {
      const s1Count = s1.split('').filter(x => x === char).length,
            s2Count = s2.split('').filter(x => x === char).length,
            maxCount = Math.max(s1Count, s2Count);

      return {
        char: char,
        count: maxCount,
        src: maxCount > s1Count ? '2' : maxCount > s2Count ? '1' : '='
      };
    })
    .filter(c => c.count > 1)
    .sort((objA, objB) => objB.count - objA.count || (objA.src + objA.char > objB.src + objB.char ? 1 : -1))
    .map(c => `${c.src}:${c.char.repeat(c.count)}`)
    .join('/');
}
const group = (a) => a.reduce((g, v) => Object.assign(g, { [v]: ++g[v] || 1 }), {})

const mix = (s1, s2) => {
  const s1_values = group(s1.match(/[a-z]/g))
  const s2_values = group(s2.match(/[a-z]/g))

  return Object.keys(Object.assign({}, s1_values, s2_values))
    .map((key) => {
      const s1len = s1_values[key] || 0
      const s2len = s2_values[key] || 0
      const l = Math.max(s1len, s2len)

      return l > 1 && `${s1len === s2len ? '=' : s1len > s2len && '1' || '2'}:${key.repeat(l)}`
    })
    .filter(Boolean)
    .sort((a, b) => a.length < b.length
      ? 1
      : a.length > b.length
        ? -1
        : a > b ? 1 : a < b ? -1 : 0
    )
    .join('/')
}