数组分组
_.chunk([...'libateer'],3) 3是指每个分组后组内元素的数量
数组剔除
_.compact(['1',0,1,2,null,123,NaN]) 数组每个值可以转换为false的值的话,就剔除这个值
数组合并
var array = [1];
var other = _.concat(array, 2, [3], [[4]]);支持混合的数组,单个数据的操作
let x=_.concat([1],2,[3,4],'asdf')
let xx=[...x,1,...[1,2],'asdf']
//内置的方法就能实现
从数组前面删除数组元素
_.drop(arr,n)
_.dropRight(arr,n)
_.dropWhile()
1.var users = [
{ 'user': 'barney', 'active': false },
{ 'user': 'fred', 'active': false },
{ 'user': 'pebbles', 'active': true }
];
console.log(_.dropWhile(users, function(o) { return o.active==false; }))
//支持传入一个函数,但是这种情况是不会把true的数组删除,只能适用于这一个场景
let z=users.filter((e)=>{
return !e.active
})
//这样的操作是支持的
1.1:或者说传入一个对象,直接删除对象
console.log(_.dropWhile(users,{ 'user': 'fred', 'active': false }))
//他这个有毒啊,只能用官方那个例子,换一个立马就不行了,关键是false的还是选不出来,还是直接用自己的比较好
let z=users.filter((e)=>{
return !e.active&&e.user=='fred'
})
2.
let newarr=users.filter((e)=>{
//filter不会改变旧的函数,也不会操作空数组,所以需要返回一个新的数组来保存新生成的数组
return e.active
})
//效果是一致的。
_.fill 数组填充
let arr=[1,2,34,5]
console.log(_.fill(arr,'@',1,10)) 支持位置填充,但是并不会扩展数组的实际长度
//原生方法:特别适合新数组的初始化
let obj={
'name':'sdfasdf'
}
console.log(arr.fill(obj,1,10))
obj.name="1111"
console.log(arr)
//也可以实现类似的对象填充,但是对象填充的是一个内存地址,修改之后原来的也是要改变的。
_.findIndex() 数组查找,第二个值是开始查找的索引
//原生方法
let arr=[1,2,34,5]
let narr=arr.find((n,index,arr)=>{
//既然他这个是可以获取到index,为什么不能返回index来模拟findIndex方法呢?return index的时候,返回的是他本来的值
return n==2
})
console.log(narr)
//返回的是找到的元素
//返回找到元素的index
let narr=arr.findIndex((n,index,arr)=>{
return n==34
})
console.log(narr)
let person= {name: 'John', age: 20};
let narr=arr.findIndex((n,index,arr)=>{
return n==this.age
},person)
//这个函数还可以传入第二个函数,来绑定对象
console.log(narr)
//升级函数的厉害
1 .可以查询NaN这个值,弥补了indexOf的不足
2 .
_.flatten():给数组里面的数组脱去一个维度
let arr=[1, [2, [3, [4]], 5],5,[6,7,8,9]]
console.log(_.flatten(arr))
//原生,判断每一个数组的类型,如果是数组的话,就单独取出来,然后在合并到原数组里面
_.flattenDeep():脱去所有
let arr=[1, [2, [3, [4]], 5],5,[6,7,8,9]]
console.log(_.flattenDeep(arr))
//递归实现
_.falttenDepth():脱到指定深度
let arr=[1, [2, [3, [4]], 5],5,[6,7,8,9]]
console.log(_.flattenDepth(arr,2))
//做个标志位,每递归一次就+1
_.toObject()
function toObject(arr){
return _.fromPairs(arr)
}
console.log(toObject([['a', 1], ['b', 2]]))
//换个名字
//原生,便利每个数组内容,检查数组的长度,关键是key值的种类,es6的语法好像扩展到可以用dom结构当作key值了
- 这个以后还要仔细琢磨一下
_.head():获取元素第一个值。。
_.initial():去掉元素最后一个值。。。真的瓜皮这个操作
_.intersection():找到所有数组共有的值,然后返回
1 .其实就是拿第一个数组里面的所有值,去后面所有数组里面进行检查,如果有的话,就返回一个新的数组
2 .Creates an array of unique values that are included in all given arrays using SameValueZero
for equality comparisons. The order and references of result values are determined by the first array.
3 .他的函数说明其实有的时候已经告诉你他的实现原理了
console.log(_.intersection([2,1,4,5,6], [2, 3,1,4],[2,1,3,4]))
..intersectionWith(objects, others, _.isEqual)
1 .效果同上,只不过每个数组里面的元素都是对象
2 .这个完全可以用来感知对象的变化,进行diff object的第一步,但是不能知道前后变化的类型,比如是增加了,还是减少了,还是乱序了。
var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 },1,{'name':'li1','age':{'age':12}}];
var others = [{ 'x': 1, 'y': 1 }, {'name':'li1','age':{'age':12}},{ 'x': 1, 'y': 2 },1,];
console.log(_.intersectionWith(objects, others, _.isEqual))
_.join():把数组里面的每一项都链接起来,变成一个字符串
let arr=['1','2','3','4']
console.log(_.join(arr,'~','@'))
//只支持传入一个参数
_.last():返回数组最后一个元素。。。
_.nth(n):获取给定索引的元素。。
1 .虽然这个可以传入负数,从后面开始i选,但是还是很简单
_.pull(arr,x,y):去掉数组里面所有的x,y
1 .检查数组里面的每个数,看他是不是x或者y,如果是,那就从数组里面删除
_.pullAll(arr,[]):和上面的一样,只不过传入参数的形式是一个数组
let arr=['1','2','3','4',2,2,3,4,5,6,3]
console.log(_.pullAll(arr,[2,3]))
_.pullAllBy():如果数组里面是对象的话,删除某个对象
var array = [{ 'x': 1,'y':3 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }];
_.pullAllBy(array, [{'y':3 },{'y':1}],'y');
//这里只能每次检查一个属性,但是值可以是很多值,不能同时检查x,y
console.log(array);
//如果自己写的话,其实可以不用传那个y的,而且也可以比较多个值
_.pullAllWidth():数组里面有对象的时候,比较多个值删除
var array = [{ 'x': 2, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 4 }];
_.pullAllWith(array, [{ 'x': 3, 'y': 4 }], _.isEqual);
//此处的{ 'x': 3, 'y': 4 }是一个整体想要实现的效果是,分别去找每一个数组里面的key-value值,如果满足给定的两个键值对之一,那么就删除整个对象。
console.log(array);
_.pullAt():删除固定索引位置的元素
var array = ['a', 'b', 'c', 'd'];
var pulled = _.pullAt(array, [1, 3]);
console.log(array);
console.log(pulled);
_.reverse()数组倒序
var array = [1, 2, 3];
_.reverse(array);
// => [3, 2, 1]
console.log(array);
//原生方法
let x=[1,2,3]
let xx=x.reverse()
console.log(xx.reverse())
//原生方法好像不是最快的
2.这个方法应该是最快的,这么土的方法竟然是最快的。。。
let x=[1,2,3]
let result=[]
let len=x.length;
for(i=len-1;i>=0;i--){
result.push(x[i])
}
console.log(result)
_.slice():对数组进行切片
直接使用原生方法
let x=[1,2,3,4,5,6,7,8,9,10,11]
let xx=x.slice(0,2)
console.log(xx)
slice不仅可以切割数组,而且可以切割字符串
_.sorted():lodash里面的排序方法目前感觉都是鸡肋啊,没有什么实质用处
- 原生方法:更高的计算效率,快速排序在实际计算机执行环境中比同等时间复杂度的其他排序算法更加的快速,而且有更低的空间成本,前者仅有O(logN)的空间复杂度,相比较后者O(n)的空间复杂度在运行的内存消耗更少。
2 .chrome对快速排序的优化:
1.10个一下的长度的小数组使用插入排序
2.快速排序最爆炸的地方:最差数组组合情况下算法会退化
3.核心在于选取一个基准,如果对一个已经有序的数组,每次选择基准元素的时候总是选择第一个或者最后一个元素,那么每次总是会有一个数区是空的,递归的层数将达到n,最后导致算法的时间复杂度退化为0N2,
4.v8中三数取中,除了头尾两个元素在额外选择一个元素参与基准元素的竞争
5.当数组长度小于1000的时候,选择中间位置的元素作为目标元素
6.当数组长度超过1000时,每隔200-215个左右的元素来确定一批候选元素,在这些数组中进行一次排序,将所得值的中位值作为目标元素
7.最后取这三个值的中卫直作为目标元素
8.
3 .火狐浏览器使用的是归并排序,只是稳定性比较好一点
4 .
1 .默认排序顺序是根据字符串unicode
2 .如果调用方法的时候没有使用参数,将按照字母顺序对数组中的元素进行排序,更准确的是按照字符编码的顺序进行排序
3 .
function init_arr(){
let re=[]
for(let i=0;i<100;i++){
re.push(Math.floor(Math.random()*100))
}
return re;
}
let re=init_arr()
function com(a,b){
return b-a;
}
let ree=re.sort(com)
//默认是从小-大,传入函数来实现自定义
// console.log(re)
// 根据数组中对象的某个值进行排序:嵌套一层函数来接收对象属性名字,其他的部分代码与正常使用sort方法相同
let arrObject=[
{name:'zopp',age:0},
{name:'gpp',age:18},
{name:'yjj',age:8}
]
function compare(property,rev){
// 添加一个参数来确定排序的顺序
if(rev){
return function(a,b){
return a[property]-b[property]
}
}else{
return function(a,b){
return b[property]-a[property]
}
}
}
console.log(arrObject.sort(compare('age',false)))
1 .V8引擎只给出了两种排序方式,小于10个元素,插入排序。大于10个元素,快速排序
2 .快速排序
3 .冒泡排序
function bubbleSort(arr) {
var len = arr.length;
for (var i = 0; i < len; i++) {
for (var j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j+1]) { //相邻元素两两对比
let temp = arr[j+1]; //元素交换
arr[j+1]=arr[j];
arr[j]=temp;
}
}
}
return arr;
}
console.log(re)
console.log(bubbleSort(re))
//改进:每一次循环的时候要正反查找,找到最大和最小的值,查找效率高一半
function bubbleSort2(arr){
console.time('改进之前的时间')
let i=arr.length-1;
while(i>0){
// console.log(i)
let pos=0;
for(let j=0;jarr[j+1]){
pos=j
//设置一个标志变量pos,用于记录每趟排序中最后一次进行交换的位置。所以下一次循环的时候,只需要扫描到pos的位置即可
let top=arr[j];
arr[j]=arr[j+1];
arr[j+1]=top;
}
i=pos;
}
}
console.log(arr)
return arr
}
// bubbleSort2(re)
function bubbleSort3(arr){
let low=0;
// 最小的初始值
let high=arr.length-1;
// 最大的初始值
let tmp,boom;
while(lowarr[j+1]){
tmp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=tmp;
}
}
// 找到最大的值
--high;
for(j=high;j>low;j--){
if(arr[j]
3 .选择排序:每次选出一个最小的值,放在最前面
function chooseSort(arr){
let len=arr.length;
let minIndex;
for(let i=0;i
4 .插入排序:打扑克的时候码牌的时候
function sortChoose(arr){
console.log(arr)
for(let i=1;i=left;j--){
arr[j+1]=arr[j]
// 给这个值腾地方,这些值直接平移
}
arr[left]=key
//把那个值换过来
console.log(arr)
// arr.splice(left,0,key)
// 为什么不能这样直接插入,而是必须要先平移
}
}
function test(arr){
console.log(arr)
for(let x=0;x<1000;x++){
let random=Math.floor(Math.random()*80)
arr.splice(random,0,random)
}
console.log(arr)
}
test(re)
//测试的时候这样是行的,那就证明大量的循环插入是没问题的啊
6 .快速排序:简单方便的超级排序方法,大量的递归不不会有问题,看一下尾递归优化的知识吧
function querkSort(arr) {
if (arr.length <= 1) { return arr; }
//这个还很关键,如果拆到最小的那一组里面有1个元素的话,那确实需要直接返回
var pivotIndex = Math.floor(arr.length / 2);
var pivot = arr.splice(pivotIndex, 1)[0];
var left = [];
var right = [];
//利用left,right来存储递归的子数组,因此需要额外的存储空间,这与理论上的平均空间复杂读差距计较大。
for (var i = 0; i < arr.length; i++){
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return quickSort2(left).concat([pivot], quickSort2(right));
};
//加强版本
function quickSort(arr, left, right) {
let len = arr.length;
let partitionIndex;
let lefta = typeof left !== 'number' ? 0 : left
let righta = typeof right !== 'number' ? len - 1 : right;
if (lefta < righta) {
partitionIndex = partition(arr, lefta, righta);
quickSort(arr, lefta, partitionIndex - 1);
quickSort(arr, partitionIndex + 1, righta);
}
return arr;
}
function partition(arr, left, right) {
var pivot = left;
let index = pivot + 1;
for (let i = index; i <= right; i++) {
if (arr[i] < arr[pivot]) {
swap(arr, i, index);
index++;
}
}
// 交换pivot和index-1索引的值之后index-1索引左边全都是小于arr[index-1]的元素;
swap(arr, pivot, index - 1);
return index - 1;
}
function swap(arr, i, j) {
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
return arr
}
let r1=[]
function init_arr(re){
for(let i=0;i<10000;i++){
re.push(Math.floor(Math.random()*10000))
}
return re
}
let rr=init_arr(r1)
console.log(quickSort(rr))
7 .希尔算法,归并排序,堆排序,桶排序,计数排序,基数排序
8 .交换数组的
9 .术语解释
1.数据规模
2.稳定:两个相等的值在排序前后相对位置是否改变,如果不改变则成为稳定,反之不稳定
3.排序方式:内排序是指所有操作都在内存中完成,外排序把数据放在磁盘中,排序通过磁盘和内存的数据传输才能进行。
4.时间复杂度:算法所执行消耗的时间
5.空间复杂度:算法执行所需的内存的大小
_sortedUniq():排序并且剔除相同的元素
1 .先返回排序的数组。
2 .然后删除相同的元素。
function sortedUniq(r1){
let index=0;
let sorted=quickSort(r1);
for(let i=0;i
_sortedUniqBy():这个以为是基于数粒度的排序比较,才发现不是。。。目前不知道这个函数的实质用途。。。
_tail():最佳鸡肋函数,去掉一个数组的头元素
_take(arr,x,y):第二鸡肋,自己实现一个算了,返回一个数组,删掉原数组下x->y之间的元素
function take(arr,x,y){
if(y>arr.length){
y=arr.length
}
if(x<0){
x=0
}
arr.splice(x,y)
return arr;
}
take([1,2,3,4,5,6],0,100)
_union(arr1,arr2):从多个数组里面选出唯一值,返回一个新的数组
1.合并所有数组,然后做去重
__.unionWith():这种复杂情况,数组里面是对象,进行对象去重
_uniq(arr):进行数组去重
1 .好像是有更简单的版本,原生语法
let x=[1,2,3,4,5,1,2,3,4,4,5,6,3]
function uniq(arr){
console.log([...new Set(arr)])
}
uniq(x)
let x=[{'name':1},{'name':1}]
function uniq(arr){
console.log([...new Set(arr)])
}
uniq(x)
2 .为什么要写之前那个呢。。。难道速度会比原生的更快么?
3 .数组里面的对象果然是不能去重的。
4 ._.uniqWith(array, [comparator])这个还是需要用下lodash
5 .这个其实也很简单的,先把对象按照数组排列,然后比较前一个值,如果有的话,就比较下一个值是否相等
6 .[x,1,y1]->{'x':1,'y':1},然后比较前一个,关键是怎么把后一个变成前一个
var articles = [{
title: 'hello',
content: 'hello world',
created_at:'2017-08-30 13:45:15'
},{
title: 'foo',
content: 'foo bar',
created_at:'2017-08-30 13:45:15'
}]
let result=[]
articles.map(item => {
let r=[]
let x=Object.values(item)
let y=Object.keys(item)
r.push(x)
r.push(y)
result.push(r)
})
console.log(result)
_zip(arr1,arr2,arr3):对数组进行一维变换,取每个数组的第x个元素,返回一个个新的数组
_unzip():上面的反向操作
_without(arr,x,y):删除数组内的x,y,返回新的数组
_zipObject(arr1,arr2):一维转换成一个对象
_.zipObjectDeep([props=[]], [values=[]]):深度转换
_.zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2]);
// => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } }
_zipWith(arr1,arr2,arr3,func):转换的时候进行一波函数操作
_.zipWith([1, 2], [10, 20], [100, 200], function(a, b, c) {
return a + b + c;
});
//[111,222]
属性
1 .constructor:返回对创建此对象数组函数的引用
2 .length:设置或者获取数组种元素的数目
3 .prototype:对对象添加属性或者方法--arr.prototype.name=20 arr可以直接设置prototype么?正确的使用方法是这样的--
原生方法
1 .Array.from():从一个类似数组或可迭代对象中创建一个新的数组实例
1 .注意后一个,如果前面传入的是一个可迭代的对象,可以传入一个函数进行再次加工,这个功能其实别的方法都可以实现
2 .console.log(Array.from('123123')) 字符串转数组,字符串根据逗号转为数组
3 .let str='12123,123123,45345,234'
console.log(str.split(','))
4 .那这个方法还有需求么?因为除去第二个特性,数组的扩展符都是可以实现的,而且这个还可以扩展函数,只要函数返回的是数组
2 .Array.isArray():用于确定传递的值是否是一个array类型,这个有统一的函数,来判断所有的值类型
3 .Array.of():用于创建一个具有可变数量参数的新数组实例,而不考虑参数的数据和类型
1.Array.of(7)-> [7]
2.Array(7)->[,,,,,,,]:创建一个长度为7的空数组
4 .Array.concat():合并两个或者多个数组,返回一个新数组
1 .扩展符也是可以返回合并数组,所以这个函数也是很鸡肋
2 .let a1=[1,2,3]
let a2=[3,4,5]
console.log(a1.concat(a2,a2,a2,a1))
5 .Array.prototype.copyWithin(target, start = 0, end = this.length)
var array1 = [1, 2, 3, 4, 5];
// place at position 0 the element between position 3 and 4
console.log(array1.copyWithin(0, 3, 4));
//从0开始,把3-4之间的元素移到0,依次往后排,会覆盖数据,此时3-4这个位置的数,如果覆盖不到的话,那还是原来的元素
// expected output: Array [4, 2, 3, 4, 5]
// place at position 1 the elements after position 3
console.log(array1.copyWithin(1, 3));
// expected output: Array [4, 4, 5, 4, 5]
1 .2,3个参数都是可以省略的,而且可以是负数,这样就其实是取的倒数
6 .Array.prototype.entries():数组和对象都可以有的一个遍历手段
let arr=[1,22,3,4,5,6,7,8]
let obj={
"name":'123',
"age":30
}
for(let [key,value] of Object.entries(obj)){
console.log(key)
console.log(value)
}
for(let [index,value] of Object.entries(arr)){
console.log(index)
console.log(value)
}
//所以以后遍历数组就使用这个方式就可以了。
7 .every():《检查》整个数组,看是否全部都满足一个条件
function t1(x){
return x>0
}
console.log(arr.every(t1))
8 .fill(n,x,y):在数组的x-y的索引全部都使用n填充
9 .区别
function f1(e){
return e>4
}
c(arr.filter(f1))
//挑选出符合条件的数组,返回的是一个新的数组
c(arr.find(f1))
//找到第一个符合条件的元素
c(arr.findIndex(f1))
//返回符合条件的元素的索引
c(arr.forEach(f1))
//对数组的每个元素都执行一次f1函数
//复杂版本,对数组里面的元素进行模糊查询
let fruits = ['apple', 'banana', 'grapes', 'mango', 'orange'];
function filterItems(query){
return fruits.filter((el)=>{
return el.toLocaleLowerCase().indexOf(query.toLocaleLowerCase())>-1
})
}
includes:进行准确的定位,看是否有完全一样想要的元素
console.log(fruits.includes('apple'))
join()数组中转字符串
let fruits = ['apple', 'banana', 'grapes', 'mango', 'orange'];
function c(x){
console.log(x)
}
let s=fruits.join(',')
//正
c(s.split(','))
//反
reduce():对累计器和数组中的每个元素从左到右应用一个函数,将其简化为单个值
1 .reducer函数的返回值分配给累计器,其值在数组的每隔迭代中被记住,并最终称谓单个值的结果值。
杂牌函数
1 .keys()
2 .lastIndexOf()
3 .map()和上面的重复了几百次
4 .pop()
5 .push()
6 .reverse()
7 .shift()
8 .unshift()
9 .toLocaleSteing()