有趣的前端编程题:携程 2018 春招

认真编写的题目能测出代码水平,而有趣的题目则能激发人的“代码欲望”。前端

前言

先后参加过网易、美团、头条、滴滴等公司的在线编程题。简单的认为题目大致有如下三种类型:git

  1. 认真编写的题目会促进答题者去认真的回答
  2. 有趣的题目会激起答题者的兴趣,以致于能激发一些『灵感』
  3. 没有意义的题目不值得答,这是一个双向选择

笔试题在不少时候确实是个“坑”,能避开就避开,由于毕竟像情况 2 的是少数但优质的题目确实是一个很好的编程能力检验,遇到了就不要错过github

2018 年春招的携程前端笔试题是一个不错的例子,下面对其中的编程题作一番分析。面试


P.S. 原文显示效果更好喔:) check:rayjune.me/有趣的前端编程题算法

做者:RayJune转载请署名,请尊重博主含辛茹苦、遍查资料、一行一行含泪码出来的成果spring

常见问题

简单列举一下可能被问到的问题:编程

  • 博主你的代码肯定 100% AC 了吗?

很是肯定 100% AC。同时这意味着细节复杂度上的处理到位。(实际前端开发中要处理大量细节复杂度的东西,这一样颇有意思)json

  • 为啥不用 ES6 来写呢?

避免有些笔试平台不支持 ES6 的情况,你懂的数组

  • 为啥不用 new Set() 来去重呢?

同上,另外顺便回顾一下数组去重。在面试的时候写 new Set 来数组去重会被 diss 的,别问我为何知道 :>函数式编程

  • 为何博主你的代码没有杂糅在一块儿,而是合理的抽出为多个函数呢?

一种编程品味

编程风格

简单陈述一下文中代码使用的编程风格:

  1. 使用 ES5,以免有些在线编程平台不支持 ES6 的情况(因此在这里没有用 new Set()
  2. Airbnb 代码规范,不使用单 var 模式
  3. 变量定义(var),函数体(body),return 值三者用空行隔开,逻辑鲜明
  4. 有意义的变量命名
  5. 适当的函数抽取,一个函数只作一件事情

另外还有 根据不一样场合使用合适的类型判断方式

  • Array.isArray 判断数组,
  • Object.prototype.toString.call 来判断纯对象
  • typeof 判断基本类型和 function
  • instanceof 来判断自定义的对象

一. 字符串截取

题目

描述

给定一个长度小于 50 且包含字母和数字的任意字符串,要求按顺序取出当中的数字和英文字母,数字须要去重,从新排列后的字符串数字在前,字母在后。

输入

须要截取的字符串(包含数字和字母)

输出

按照要求从新排列的字符串

样例输入

'携程C2t0r1i8p2020校招'
复制代码

样例输出:

'2018Ctrip'
复制代码

解答

确定有同窗表示第一题不值得分析。但我仍是想抛砖引玉一下,思路以下:

  1. 字符串转数组str.split('')进而使用数组的各类操做方法,如 arr.forEach
  2. 判断字符串值是否为数字/\d/.test(element) 或者 Number.isNaN(Number(element)
  3. 判断字符串值是否为字母/[a-zA-Z]/.test(element)
  4. 数字字符串去重(利用数组去重)
  5. 输出数字+字母

由此有了这一版代码:

条件 1~3

function handleStr(str) {
  var arr = str.split('');
  var nums = '';
  var words = '';
    
  arr.forEach(function (element) {
    if (/\d/.test(element))) {
      nums += element;
    } else if (/[a-zA-Z]/.test(element) ) {
      words += element;
    }
  });

  return uniqueStr(nums) + words;
}
复制代码

去重

做为前端开发超高频面试题,相信你们早已对数组去重熟捻于心:

基本类型去重:

function unique(arr) {
  return arr.filter(function (element, index) {
    return arr.indexOf(element) === index;
  });
}
复制代码

基本+复杂类型去重:

function unique(arr) {
  var hash = {};

  return arr.filter(function (element) {
    if (hash.hasOwnProperty(element)) {
      return false;
    }
    hash[element] = true;

    return true;
  });
}
复制代码

因为数字去重(str,基本类型)基于数组去重,咱们要对原来的数组去重作一点修改:

function uniqueStr(str) {
  var arr = str.split('');

  return arr.filter(function (element, index) {
    return arr.indexOf(element) === index;
  }).join('');
}
复制代码

string.split()array.join() 帮助咱们自由的游走在字符串和数组间。

最终解答 1

function handleStr(str) {
  var arr = str.split('');
  var nums = '';
  var words = '';
    
  arr.forEach(function (element) {
    if (/\d/.test(element)) {
      nums += element;
    } else if (/[a-zA-Z]/.test(element) ) {
      words += element;
    }
  });

  return uniqueStr(nums) + words;
}

function uniqueStr(str) {
  var arr = str.split('');

  return arr.filter(function (element, index) {
    return arr.indexOf(element) === index;
  }).join('');
}

// 测试
console.log(handleStr('携程C2t0r1i8p2020校招'));
// 2018Ctrip
复制代码

最终解答 2

很是感谢评论区 @while大水逼 大神的宝贵建议,博主笔试的时候没想到 str.match(regex) 的方法。实现更简洁、逻辑的展现更好,在此补上:

function handleStr(str) {
  var nums = str.match(/\d/g).join('');
  var words = str.match(/[a-zA-Z]/g).join('');

  return uniqueStr(nums) + words;
}

function uniqueStr(str) {
  var arr = str.split('');

  return arr.filter(function (element, index) {
    return arr.indexOf(element) === index;
  }).join('');
}

// 测试
console.log(handleStr('携程C2t0r1i8p2020校招'));
// 2018Ctrip
复制代码

二. 数组升维

题目

描述

对一维数组,根据 type 类型分组成二维数组

输入

  • 输入的参数多是空数组 [],空对象 nullundefined,数字,字符串等异常值;
  • 也多是结构为 [{ type, content}] 的有效值;
  • 甚至是 [null, null, (type, content)] 等有效和非法值混合的数据。

输出

  • 当输入数据不合法时,输出空数组 []
  • 当输入数据有效时(请先过滤数组里的异常元素),而后将相同 type 值的元素合并,造成新元素 {"type": "A", "contents": [content1, content2]},其中,contents 为一个数组,元素为全部 type 值相同的 content 值。
  • 注意,输出的是一个标准 JSON 格式

样例输入

var input = [null, 2, "test", undefined, {
  "type": "product",
  "content": "product1"
}, {
  "type": "product",
  "content": "product2"
}, {
  "type": "tag",
  "content": "tag1"
}, {
  "type": "product",
  "content": "product3"
}, {
  "type": "tag",
  "content": "tag2"
}];
复制代码

样例输出

[{"type":"product","contents":["product1","product2","product3"]},{"type":"tag","contents":["tag1","tag2"]}]
复制代码

解答

乍一看要求颇多,咱们一点点来拆解:

条件 1

当输入数据不合法时,输出空数组 []

什么数据不合法?输入值不为 JSON 格式(即 array 类型)

还有呢?输入值为 JSON 格式(即 array 类型),但长度为 0;

由此写下第一句:

function groupList(list) {
  if (!Array.isArray(list) || list.length === 0) { return []; }
}
复制代码

条件 2

当输入数据有效时(请先过滤数组里的异常元素)

过滤掉[],空对象 nullundefined,数字,字符串等异常元素:

function groupList(list) {
  if (!Array.isArray(list) || list.length === 0) { return []; }
  
  var validItems = getValidItems(list);
}

function getValidItems(json) {
  return json.filter(function (element) {
    return isPureObject(element)
  });
}

function isPureObject(item) {
  return Object.prototype.toString.call(item).slice(8, -1) === 'Object';
}
复制代码

条件 3(隐藏条件)

且慢,结构不为 { "type": "xx", "content": "yy" } 的值,是否是也为异常元素呢?为此在 getValidItems 里加上一句:

function getValidItems(json) {
  return json.filter(function (element) {
    return isPureObject(element) && element.type && element.content;
  });
}
复制代码

可能有同窗会问,这里为何不用 typeof 判断 object,而是那么麻烦撸了一个 isPureObject 呢?

首先明确 typeof 用来判断基本类型和 function(check: 根据不一样场合使用合适的类型判断方式),举个例子:

var demo = [1, 2];

demo.type = '我实际上是数组';
demo.content = '但我也有 type 和 content 属性,判断不出来了吧';
复制代码

若是是

function getValidItems(json) {
  return json.filter(function (element) {
    return typeof element === 'object' && element.type && element.content;
  });
}
复制代码

显然没法过滤 demo 这种状况了,更况且还能够是 regex 等各类非 function 的对象。

因此在线编程题请慎重考虑边缘状况。

条件 4

过滤完成后,将相同 type 值的元素合并,造成新元素

function groupList(list) {
  if (!Array.isArray(list) || list.length === 0) { return []; }
  
  var validItems = getValidItems(list);
  var result = {};

  validItems.forEach(function (item) {
    if (result.hasOwnProperty(item.type)) {
      result[item.type].push(item.content);
    } else {
      result[item.type] = [];
      result[item.type].push(item.content);
    }
  });

  return result;
}
复制代码

条件 5

貌似咱们已经完成了将相同 type 值合并这一步骤,可是:

当前的结构是 {type1: contentsArr1, type2: contentsArr2} 的结构,与题目要求的:[{type1: contentsArr1}, {type1: contentsArr2}] 不相同。

不难,再加一步即是:

function adjustFormat(obj) {
  var result = [];

  Object.keys(obj).forEach(function (type) {
    result.push({ type: type, contents: obj[type] });
  });

  return result;
}
复制代码

且慢,根据一个数组产生一个新的数组,用 array.map 是否是更物尽其用呢?(再次感谢评论区 @while大水逼 提出的方案)

更纯粹的函数式编程,更直观的逻辑展现:

function adjustFormat(obj) {
  return Object.keys(obj).map(function (type) {
    return { type: type, contents: obj[type] };
  });
}
复制代码

最终解答

完整的代码:

function groupList(list) {
  if (!Array.isArray(list) || list.length === 0) { return []; }
  
  var validItems = getValidItems(list);
  var result = {};

  validItems.forEach(function (item) {
    if (result.hasOwnProperty(item.type)) {
      result[item.type].push(item.content);
    } else {
      result[item.type] = [];
      result[item.type].push(item.content);
    }
  });

  return adjustFormat(result);
}

function getValidItems(json) {
  return json.filter(function (element) {
    return isPureObject(element) && element.type && element.content;
  });
}

function isPureObject(item) {
  return Object.prototype.toString.call(item).slice(8, -1) === 'Object';
}

function adjustFormat(obj) {
  return Object.keys(obj).map(function (type) {
    return { type: type, contents: obj[type] };
  });
}

// test
var input = [null, 2, "test", undefined, {
    "type": "product",
    "content": "product1"
},  {
    "type": "product",
    "content": "product2"
},  {
    "type": "tag",
    "content": "tag1"
}, {
    "type": "product",
    "content": "product3"
}, {
    "type": "tag",
    "content": "tag2"
}];

console.log(JSON.stringify(groupList(input)));
// [{"type":"product","contents":["product1","product2","product3"]},{"type":"tag","contents":["tag1","tag2"]}]
复制代码

总结

回到文章题目自己上来,什么算是有趣的前端笔试题

  • 不是固定套路的,不考“背诵能力”的
  • 体现 JS 能力的,考察点普遍全面
  • 和前端密切相关的和实际开发有联系
  • 注重细节复杂度,而不是纯粹的“算法复杂度”(仅针对前端开发,仅为博主一家之言)

若是是现场面试手写编程题,我以为应该再加上一条:

  • 体现编程素养的,注重纵向拓展

若是哪家公司有这样的编程题,请把我一波流内推带走 :)


更多信息,check: rayjune.me/about


有同窗问为神马没有第三题的题解,由于本身在第二题的细节复杂度中消耗了大量时间(盯了一二十分钟才发现须要进行 Array.isArray(input) 的判断) - =(相信有很多同窗也是这样),没有时间把第三题撸出来了。。。

相关文章
相关标签/搜索