前端中的簡單編程題-數組(1)

一邊學習前端,一邊經過博客的形式本身總結一些東西,固然也但願幫助一些和我同樣開始學前端的小夥伴。前端

若是出現錯誤,請在評論中指出,我也好本身糾正本身的錯誤node

author: thomaszhou編程

數組的內置方法應用

判斷是不是數組類型

不少時候咱們須要對JavaScript中數據類型( Function、String、Number、Undefined、Boolean和Object)作判斷。在JavaScript中提供了typeof操做符能夠對這些經常使用的數據類型作判斷。但要使用typeof來判斷數據是否是一個數組,就不起做用了數組

console.log(typeof function () {return;}); // function
	 console.log(typeof "a"); // string
	 console.log(typeof 123); // number
	 console.log(typeof a); //undefined
	 console.log(typeof true); // boolean
	 console.log(typeof NaN); // number
	 console.log(typeof !NaN); //boolean
	 console.log(typeof {name:"leo",age: "37"}); // object
	 console.log(typeof ["leo","37"]); // object
	 console.log(typeof null); // object
複製代碼

es5的isArray()函數

var arr = [1,2,3,45];
function isArray(obj){
    return Array.isArray(obj);
	}
alert(isArray(arr));
複製代碼

ECMAScript 5 還有着很是大的兼容性,因此咱們並不能完美的使用原生判斷,當使用ie6/7/8的時候就會出現問題。瀏覽器

construct和instanceof

  • constructor屬性返回對建立此對象的函數的引用,使用此屬性能夠檢測數組類型
  • 除了使用constructor自身屬性以外,還可使用instanceof。 instanceof用來判斷某個構造函數的prototype是否存在於被檢測對象的原型鏈裏。也就是判斷前面的對象是不是後者對象的類或者實例。
var arr = [1,2,3,45];
console.log(arr.constructor === Array);

console.log(arr instanceof Array);
複製代碼
  • 缺點:須要注意的是,當在不一樣的window或iframe裏構造的數組時會失敗。這是由於每個iframe都有它本身的執行環境,彼此之間並不共享原型鏈,因此此時的判斷一個對象是否爲數組就會失敗。此時咱們有一個更好的方式去判斷一個對象是否爲數組。 當你在多個frame中回來跳的時候,這兩種方法就慘了。因爲每個frame都有本身的一套執行環境,跨frame實例化的對象彼此並不共享原型鏈,經過instanceof操做符和constructor屬性檢測的方法天然會失敗。

Object.prototype.toString

var is_array = function(obj){
    return Object.prototype.toString.call(obj) === '[object Array]';
};
	
alert(is_array(arr));
複製代碼

使用Object.prototype.toString方法來檢測對象類型。toString將返回[object type] type爲對象類型。下面是對象檢測顯示的結果。bash

var toString = Object.prototype.toString; 
     console.log(toString.call(dd));//[object Function] 
	 console.log(toString.call(new Object));//[object Object] 
	 console.log(toString.call(new Array));//[object Array] 
	 console.log(toString.call(new Date));//[object Date] 
	 console.log(toString.call(new String));//[object String] 
	 console.log(toString.call(Math));//[object Math] 
	 console.log(toString.call(undefined));//[object Undefined] 
	 console.log(toString.call(null));//[object Null]
複製代碼

所以咱們能夠利用對象的Object.prototype.toString方法檢測對象是否爲數組。在frame下也經過。app

  • 最佳檢測 -Array和Object.prototype.toString結合

最佳檢測方法就是,無論原生isArray是否可用,都回歸到object.prototype.toString的檢測上。dom

  • Object.prototype.toString的行爲:首先,取得對象的一個內部屬性[[Class]],而後依據這個屬性,返回一個相似於"[object Array]"的字符串做爲結果(看過ECMA標準的應該都知道,[[]]用來表示語言內部用到的、外部不可直接訪問的屬性,稱爲「內部屬性」)。利用這 個方法,再配合call,咱們能夠取得任何對象的內部屬性[[Class]],而後把類型檢測轉化爲字符串比較,以達到咱們的目的。
  • call改變toString的this引用爲待檢測的對象,返回此對象的字符串表示,而後對比此字符串是不是[object Array],以判斷其是不是Array的實例。爲何不直接o.toString()?嗯,雖然Array繼承自Object,也會有toString方法,可是這個方法有可能會被改寫而達不到咱們的要求,而Object.prototype則是老虎的屁股,不多有人敢去碰它的,因此能必定程度保證其「純潔性」:)

JavaScript 標準文檔中定義: [[Class]] 的值只多是下面字符串中的一個:Arguments, Array, Boolean, Date, Error, Function, JSON, Math, Number, Object, RegExp, String。函數

var isArray = function () {
    		if (Array.isArray) {
    			return Array.isArray;
    	    }
    	    //下面語法不明白
    		var objectToStringFn = Object.prototype.toString, arrayToStringResult = objectToStringFn.call([]);
    		return function (subject) {
    				return objectToStringFn.call(subject) === arrayToStringResult;
    	    };
  }();
  alert(isArray(arr));// true
複製代碼

複製數組

咱們都知道數組是引用類型數據。這裏使用slice,map複製一個數組,原數組不受影響。性能

let list1 = [1, 2, 3, 4];
let newList = list1.slice();//推薦slice
list1.push(5); // [1,2,3,4,5]
console.log(newList); //[1,2,3,4]
console.log(list1); //[1, 2, 3, 4, 5]

let list2 = [5,6,7,8];
let newList2 = list2.concat();
newList2.push(9); // 
console.log(newList2); //[5, 6, 7, 8, 9]
console.log(list2); //[1, 2, 3, 4, 5]
複製代碼

轉換成數組

類數組(NodeList)轉數組(Array),函數參數轉數組

  • 經過三種方式實現:如slice,這裏加多數組的from方法,ES6語法糖
getElementsByClassName獲取的dom元素的數據類型爲HTMLCollection,雖然相似於數組能夠遍歷,可是該數據類型不存在數組中用於操做數組的方法,經過y如下方法轉換後,就可使用數組的內置方法

//返回的不是真正的Array(你沒法使用filter、map、reduce等方法)
const nodeList = document.querySelectorAll('div');  
// 方法1: 使用Array.from
const arrayList1 = Array.from(nodeList);
// 方法2: 使用slice
const arrayList2 = Array.prototype.slice.call(nodeList);
// 方法3: 使用ES6語法糖
const arrayList3 = [...nodeList];
複製代碼
  • 函數參數 轉成 數組

函數參數轉數組 將函數參數轉數組,利用arguments僞數組形式,再用slice拷貝爲新數組。

function argsParam() {
     //arguments僞數組形式,再用slice拷貝爲新數組
     return Array.prototype.slice.call(arguments);
}
 
console.log(argsParam(1,2,3,4)); //[1, 2, 3, 4]
複製代碼
  • 那Array.Prototype.slice的內部實現是什麼?
Array.prototype.slice = function(start,end){
       var result = new Array();
       start = start || 0;
       end = end || this.length; //this指向調用的對象,當用了call後,可以改變this的指向,也就是指向傳進來的對象,這是關鍵
       for(var i = start; i < end; i++){
            result.push(this[i]);
       }
       return result;
    }
複製代碼
  • 其餘:Array.prototype.slice.call能將具備length屬性的對象轉成數組,除了IE下的節點集合(由於ie下的dom對象是以com對象的形式實現的,js對象與com對象不能進行轉換)
var a = {length:2, 0:'zero', 1: 'first'};
    console.log(Array.prototype.slice.call(a));  // ["zero", "first"]

    var b = {length:3, 0:'zero', 1: 'first', 2 : 'zero'};
    console.log(Array.prototype.slice.call(b));  // ["zero", "first", "zero"]

    var c = {length:3, 2 : 'zero'};
    console.log(Array.prototype.slice.call(c));  //  [empty, empty , "zero"]

複製代碼

重複n個字符

  • 利用Array構造函數傳參,再使用join函數分隔指定的字符串
/**
    @params
    num: 重複次數
    str: 重複字符串
**/
function repeatStr(num, str) {
    return new Array(num+1).join(str);
}

console.log(repeatStr(5, 's'));//sssss
複製代碼

建立n乘n二位矩陣

建立n乘n二維矩陣,並初始化數據 使用Array對象傳入數組length參數,調用fill再用map循環fill替換對應的值返回一個新數組

/**
    @params
    num: 矩陣次數
    str: 矩陣數組中的值,因爲fill函數替換因此值都是一致的
**/
function arrayMatrix(num, matrixStr) {
    return Array(num).fill(null).map(() => Array(num).fill(matrixStr));
}
//  ["a", "a", "a", "a"]  ["a", "a", "a", "a"] ["a", "a", "a", "a"] ["a", "a", "a", "a"]
console.log(arrayMatrix(4, 'a'));
複製代碼

數組去重

!!! 方法12345對於數字1和字符串1這類的問題,是沒法去重的,可是方法6能夠解決這個問題,可是也會致使須要設置最後是留下字符串的1仍是數字1

  • 法一:雙層for循環(不用新的內存空間-可是時間複雜度很高)
    • 第一層遍歷數組,第二層用第一層獲得的值來和後面全部的值依次比較,相同則用splice()方法從數組中提除該重複元素。
  • 法二:遍歷數組法(不用新的內存空間-可是時間複雜度很高)
    • 實現思路:新建一新數組,遍歷傳入數組,遍歷的值若是和新數組內的元素不重複,就加入該新數組中;注意:判斷值是否在數組的方法「indexOf」是ECMAScript5 方法,如下不支持,需多寫一些兼容低版本瀏覽器代碼,
    • indexOf就是找到新數組的第一個某值,方便查找是否重複
function unique1(array){ 
var n = []; //一個新的臨時數組 
//遍歷當前數組 
for(var i = 0; i < array.length; i++){ 
//若是當前數組的第i已經保存進了臨時數組,那麼跳過, 
//不然把當前項push到臨時數組裏面 
if (n.indexOf(array[i]) == -1) 
n.push(array[i]); 
} 
return n; 
}

複製代碼

經過unique1.apply(arr)或unique1.call(arr)調用,爲何?

  • 法三:數組下標判斷法
    • 仍是得調用「indexOf」性能跟遍歷數組法差很少,實現思路:若是當前數組的第i項在當前數組中第一次出現的位置不是i,那麼表示第i項是重複的,忽略掉。不然存入結果數組
unction unique3(arr){
       var n = [];
       n.push(arr[0]);
       len = arr.length;
       //從第二項開始遍歷 
       for(var i = 1; i < len; i++) {
       //若是當前數組的第i項在當前數組中第一次出現的位置不是i, 
        //那麼表示第i項是重複的,忽略掉。不然存入結果數組 
           if (arr.indexOf(arr[i]) == i) {
               n.push(arr[i]);
           }
       }
       return n;
   }
   console.log(unique3(arr));
複製代碼
  • 方法四:排序後相鄰去除法
    • 雖然原生數組的」sort」方法排序結果不怎麼靠譜,但在不注重順序的去重裏該缺點毫無影響。實現思路:給傳入數組排序,排序後相同值相鄰,而後遍歷時新數組只加入不與前一值重複的值。
function unique4(array){ 
    array.sort(); 
    var re=[array[0]]; 
    for(var i = 1; i < array.length; i++){ 
        if( array[i] !== re[re.length-1]){ 
            re.push(array[i]); 
        } 
    } 
    return re; 
} 
複製代碼
  • 法五:對象鍵值對法(最優):思路以下
    • 建立一個JavaScript對象以及新數組
    • 使用for循環遍歷原數組,每次取出一個元素與JavaScript對象的鍵作對比
    • 若是不包含,將存入對象的元素的值推入到結果數組中,而且將存入object對象中該屬性名的值設置爲1
//    use 'strict';
var arr = ['1',1,2,3,4,4,45,65,'b','a','b'];
function unique4 (arr) {
    var newArr = [],// 構建一個新數組存放結果
        object = {};
    len = arr.length;
    // for循環時,每次取出一個元素與對象進行對比 
    // 若是這個元素不重複,則將它存放到結果數中 
    // 同時把這個元素的內容做爲對象的一個屬性,並賦值爲1, 
    // 存入到第2步創建的對象中
    for (var i = 0; i < len; i++) {
    // 檢測在object對象中是否包含遍歷到的元素的值
        if (!object[typeof arr[i] + arr[i]]) {
        // 若是不包含,將存入對象的元素的值推入到結果數組中
            object[typeof arr[i] + arr[i]] = 1
            //存入object對象中該屬性名的值設置爲1
            newArr.push(arr[i]);
        }
    }
//    console.log(object);
    return newArr;
}
console.log(unique4(arr));
複製代碼

test:

var arr = [1,2,3,4,'a','b',1,3,4,56,32,34,2,'b','c',5,'1','2'];
arr.unique3(); // [1, 2, 3, 4, "a", "b", 56, 32, 34, "c", 5, "1", "2"]
複製代碼

不一樣的鍵可能會被誤認爲同樣;例如: a[1]、a["1"] 。這種方法所費時間:621ms。這種方法所費時間是最短,但就是佔用內存大一些

  • 法六:不區分字符串和數字的去重(對象鍵值法)
    • 對象的key,在存入的時候,就自動轉化成string類型,因此像前四種方法針對1和'1'是沒法區分,可是若是將值放進對象的key中,1和'1'的比較就會編程'1'和'1'的比較,也就是i 字符串間的比較
function unique4 (arr) {
       var newArr = [],
           object = {};
       len = arr.length;
       for (var i = 0; i < len; i++) {
         if (!object[arr[i]]) {
           object[arr[i]] = 1;
           newArr.push(arr[i]);
         }
       }
       console.log(object);
       return newArr;
   }
   console.log(unique4(arr));
複製代碼
  • ES6去重

注意set返回的也是一個不重複的類數組形式要使用Array.from或者別的方法方法轉成數組形式

function unique (arr) { 
    return Array.from(new Set(arr)) 
}
或者:
const unique = arr => [...new Set(arr)];
複製代碼

Array union (合併數組去重)

用 a 和 b 的全部值建立一個 Set 並轉換成一個數組。

const union = (a, b) => Array.from(new Set([...a, ...b]));
// union([1,2,3], [4,3,2]) -> [1,2,3,4]
複製代碼

過濾數組中的非惟一值

  • 將Array.filter()用於僅包含惟一值的數組。
  • 思路:indexOf和lastIndexOf找的位置相同,那就是惟一的值
const filterNonUnique = arr => arr.filter(i => arr.indexOf(i) === arr.lastIndexOf(i));

// console.log(filterNonUnique([1,2,2,3,4,4,5])); // [1,3,5]
複製代碼
相關文章
相關標籤/搜索