有趣的前端編程題:攜程 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) 的判斷) - =(相信有很多同窗也是這樣),沒有時間把第三題擼出來了。。。

相關文章
相關標籤/搜索