如何簡單並優雅的處理一類操做數組元素的問題

本期題目

「 題目 」: 刪除給定一維數組(暫不考慮多維)元素中不符合條件的元素,結果返回新的數組

首先,咱們在實際開發中會遇到不少處理數組的場景,其實無非就是四個字——"增刪查改"。javascript

可是,真的只是簡單的增刪查改嗎?java

NO!數組

若是隻是這麼簡單,那寫這篇博文就有點多此一舉了!函數

下面讓咱們分析一下題目所涉及的知識點:性能

考點思考


  1. 數組的遍歷、處理;
    1. 遍歷:使用循環遍歷數組;
    2. 循環使用 while 便可,結合判斷函數的元素判斷的返回值;
  2. 對元素的判斷處理;
    1. 傳遞一個判斷函數(能夠靈活定義,拓展性比較好);
    2. 判斷函數:
      1. 符合條件的返回 true;
      2. 不符合條件的返回 false

相關回顧


讓咱們簡單回顧一下數組 Array 對象的方法:

  • push( ):向數組的末尾添加一個或更多元素,並返回新的長度;
  • unshift( ):向數組的開頭添加一個或更多元素,並返回新的長度;
  • pop( ):刪除並返回數組的最後一個元素;
  • shift( ):刪除並返回數組的第一個元素;
  • concat( ):鏈接兩個或更多的數組,並返回結果;
  • join( ):把數組的全部元素放入一個字符串。元素經過指定的分隔符進行分割;
  • reverse( ):顛倒數組中元素的順序;
  • sort( ):對數組的元素進行排序;
  • slice( ):從某個已有的數組返回特定的元素;
  • splice( ):刪除元素,並向數組添加新的元素;
  • valueOf( ):返回數組對象的原始值;
  • toString( ):把數組轉化爲字符串,並返回結果;
  • toSource( ):返回該對象那個的源碼;
  • toLocaleString( ):把數組轉化爲本地數組,並返回結果;

PS:以上數據來自 W3school測試

其次,咱們分析一下,題目是對數組元素的刪除,有關 Array 對象的刪除操做就是如下的四種方法:ui

  1. pop( ):刪除並返回數組的最後一個元素;
  2. shift( ):刪除並返回數組的第一個元素;
  3. slice(startIndex,endIndex):從某個已有的數組返回特定的元素;
  4. splice(index,howmany,newitem01,newitem02... ):刪除/添加元素,並向數組添加新的元素;

簡單分析一下區別:spa

方法名稱 方法介紹 是否改變原數組 返回值 備註(適用場景)
pop(  ) 末尾刪除 末尾元素
獲取數組元素
末尾依次刪除數組元素
shift(  ) 開頭刪除 開頭元素
獲取數組元素
開頭依次刪除數組元素
slice(  ) 特定截取 不會 一個新數組 返回新的數組,不改變原數組
splice(  )
特定刪除
特定添加
處理後的原數組 直接改變原有數組

再讓咱們回顧一下常見的幾種循環

JavaScript 常見的三種循環

  1. while

  2. do while

  3. for

while 循環

語法結構與使用步驟:
// 語法結構
while(/**條件**/){
// 代碼塊;改變條件
}

// 使用步驟
// 1. 初始化變量
// 2. 判斷條件
// 3. 執行代碼塊
// 4. 改變初始條件
// 5. 判斷條件

// 示例
var i=0,
      _arr=[0,1,2,3,4,5];

while(i<=_arr.length-1){
    // 作點什麼
    console.log(i);
    // 改變初始條件
    i--;
}
// 結果
// 0,1,2,3,4,5
複製代碼

do while 循環

語法結構與使用步驟
// 語法結構
do {
// 代碼塊
} while (// 條件);

// 使用步驟:
// 1.初始化變量
// 2.無條件執行一次
// 3.判斷條件
// 4.執行代碼塊
// 5.改變初始條件
// 6.判斷條件

// 示例
var i=0,
    _arr=[0,1,2,3,4,5,6,7,8];

do {
    // 作點什麼
    console.log(_arr[i]);
    i++;
} while(i<=_arr.length)

// 結果
// 0,1,2,3,4,5,6,7,8

複製代碼

for 循環

語法結構與使用步驟
// 語法結構
for(//初始化變量;//判斷條件;//改變初始化變量){
// 代碼塊
}

// 使用步驟
// 1. 初始化變量(只會執行一次)
// 2. 判斷條件
// 3. 執行代碼塊
// 4. 改變初始化變量(在循環體裏面去作這件事,避免死循環)
// PS:建議不要嵌套多層循環(三層以上),
// 防止處理邏輯複雜,易進去死循環,形成CPU使用率飆升,
// 內存泄漏,要考慮代碼執行的性能問題

// 示例
// for循環的較優寫法
var i,
    _arr=[1,3,6,12,3,7];

for(i=_arr.length;i--;){
    // 使用_arr[i]作點什麼吧
    console.log(_arr[i]);
}
// 結果
// 7,3,12,6,3,1

複製代碼
  • do while 會無條件執行一次,再進行條件判斷
  • 使用 while 時不知道循環次數,使用 for 時知道循環次數
  • while 是條件循環,for 是限定了循環次數

代碼實現


結合以上基本知識點回顧與分析,現給出如下的實現方案:debug

  1. 定義一個 dropElements 方法:兩個傳參 arr,judgefn;
    1. arr:原數組參數;
    2. judgefn:條件判斷函數[靈活定義判斷條件,結果 return true/false];
  2. 對傳參 arr,進行代碼健壯性測試:類型/空值判斷;
  3. 循環對 arr[0]作判斷;
  4. 當檢測到數組元素不符合 judgefn 中的條件時,對數組進行截取操做;
  5. 截取使用 _arrNew = arr.slice(1); // Array.slice(stratindex,[endindex]);
  6. 截取後的新數組賦值給中間臨時變量_arrNew;
  7. 最後 return 處理後的 _arrNew
// 代碼以下 :
function dropElements(_arr, judgefn) {
	// arr健壯性
	// 1.是不是數組
	if (!Array.isArray(_arr)) {
		// console.error("不是數組,請檢查參數!");
		return false;
		// 若是是須要對結果作容錯處理,能夠直接返回空數組 : [];
		// return [];
	}
	// 2.是否爲空,爲空時,直接返回原值
	else if (_arr.length === 0) {
		console.debug("數組爲空,原值返回!");
		return _arr;
	}

	// 數組元素判斷處理
	// 判斷參數是不是空數組,同時判斷是否符合 judgeefn 的判斷條件
	// 不是空數組的話,而且不知足條件的話--judgefn(_arr[0])爲false時,作截取操做並返回新的數組;
	let _arrNew;
	while (_arr.length > 0 && !judgefn(_arr[0])) {
		_arrNew = _arr.slice(1);
	}
	// 返回結果
	return _arrNew;
}
複製代碼

結果測試


function dropElements(_arr, judgefn) {
	// 健壯性
	// 1.是不是數組
	if (!Array.isArray(_arr)) {
		// console.error("不是數組,請檢查參數!");
		return false;
	}
	// 2.是否爲空,爲空,直接返回原值
	else if (_arr.length === 0) {
		console.debug("數組爲空,原值返回!");
		return _arr;
	}

	// 數組元素判斷處理
	// 判斷參數是不是空數組,同時判斷是否符合 judgeefn 的判斷條件
	// 不是空數組的話,而且不知足條件的話--judgefn(_arr[0])爲false時,作截取操做並返回新的數組;
	while (_arr.length > 0 && !judgefn(_arr[0])) {
		_arr = _arr.slice(1);
	}
	// 返回結果
	return _arr;
}

// _arr健壯性測試:String,Object,Array,Boolean,Null,Undefined,Number,Symbol

// 測試環境(參數,方法)搭建
var _undefined, // 提早定義基本類型Undefined的數值
	// 條件判斷函數
	testJudgeFn = (n) => n >= 3,
	// 測試數據
	testData = [
		{ type: "string", value: "_string", result: "" },
		{ type: "object", value: { a: 1, b: 2 }, result: "" },
		{ type: "array", value: [0, 1, 2, 3, 4, 5, 6, 7, 8], result: "" },
		{ type: "boolean", value: true, result: "" },
		{ type: "boolean", value: false, result: "" },
		{ type: "null", value: null, result: "" },
		{ type: "number", value: 100, result: "" },
		{ type: "number", value: 0, result: "" },
		{ type: "number", value: NaN, result: "" },
		{ type: "undefined", value: _undefined, result: "" },
	];

// 結果測試並打印
for (let i = 0, testLength = testData.length; i < testLength; i++) {
	testData[i].result =
		dropElements(testData[i].value, testJudgeFn) === false
			? `Params Type Error:${testData[i].type}`
			: dropElements(testData[i].value, testJudgeFn);

	// 打印結果
	console.table(`第${i + 1}次測試結果:`, testData[i].result);
}
複製代碼

貼出測試結果的截圖:code

測試結果

對於這個簡單的數組問題,你想到了什麼?

 小總結


你是否會有着如下問題:

  • 爲何使用傳遞一個函數而不是把具體判斷條體放進函數體內部?
  • 爲何使用 while 循環,而不是 for 循環等其餘方式?
  • 爲何還要考慮它的傳參類型,空值,容錯...等等?

簡單解答:

  • 定義一個判斷函數,是爲了增長它的判斷靈活度,可能它的指望是返回數組中的字符串對象...;
  • 循環的使用應該根據實際處理的方式,選擇相應的,而不是用到循環就是 for 循環,還有時候 do while,while 循環相比 for 循環更優;
  • 對於與一個函數,它的傳參多數狀況下是不可控制,必要的類型校驗,空值校驗處理,容錯處理,代碼的健壯性都須要考慮,只有這樣才能夠保證代碼邏輯的穩定性...

更深的思考:

  1. 數組的 slice()、splice()用法,還使用在哪些具體的實際應用,它的高級使用有哪些?
  2. 爲何使用單 var 的變量命名方式,有什麼好處?
  3. 通常咱們是如何寫 for 循環,如何結合實際場景寫一個較優的 for 循環處理邏輯?
  4. 如何拆分一個功能,實現高內聚低耦合、細分肯定功能邏輯的單一職責...
相關文章
相關標籤/搜索