- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我试图根据深度嵌套在对象中的键值对包含大约 100 个大型实体(具有近 30 个键)的对象数组进行排序,为此我使用了 lodash 的 orderBy 方法:
let name = (user) => user.accountDetails.name.toLowerCase();
let dob = (user) => user.personalProfile.dob;
orderBy(cloneDeep(data), [name, dob], [sortOrder1, sortOrder2])
*考虑将 sortOrder 设为desc 或 asec
但是排序过程花费的时间相当长。哪一种我们可以使用更快的方法对对象数组进行排序 key 埋在物体深处?
示例数据(考虑至少有 40 个键的 50 个条目)
{
"list": "bugs42",
"start-date": "2015-08-27",
"accountDetails": {
"name": "diamond",
"text": "8 months",
"milliseconds": 19936427304
}
"personalProfile": {
"name": "stark",
"dob": "2003-03-12T09:26:39.980Z",
}
},
{
"list": "bugs50",
"start-date": "2015-08-27",
"accountDetails": {
"name": "ruby",
"text": "8 months",
"milliseconds": 19936427305
}
"personalProfile": {
"name": "warmachine",
"dob": "2007-03-31T09:26:39.980Z",
}
}
最佳答案
sort()
函数我们可以使用 JavaScript 的内置数组 sort()
方法,它可以非常快速、很好地对所有内容进行排序。如果您希望原始数组保持不变,那么在数组的副本而不是数组本身上运行 sort()
方法很重要。我们可以通过几种非常简单的方式做到这一点:
array.slice.sort(…)
[...array].sort(...)
在我下面的示例中,我选择使用 spread syntax , 后一个选项:
const data = [{
list: "bugs42",
startdate: "2015-08-27",
accountDetails: { name: "diamond", text: "8 months", milliseconds: 19936427304 },
personalProfile: { name: "stark", dob: "2003-03-12T09:26:39.980Z" }
}, {
list: "bugs50",
startdate: "2015-08-27",
accountDetails: { name: "ruby", text: "8 months", milliseconds: 19936427305 },
personalProfile: { name: "warmachine", dob: "2007-03-31T09:26:39.980Z" }
}];
const sortByDobAsc = data => [...data].sort((a,b) => new Date(a.personalProfile.dob) - new Date(b.personalProfile.dob));
const sortByDobDes = data => [...data].sort((a,b) => new Date(b.personalProfile.dob) - new Date(a.personalProfile.dob));
console.log(sortByDobAsc(data), sortByDobDes(data));
有关 JavaScript 的内置 sort()
方法的更多信息,请查看此处的 MDN 文档:Array.prototype.sort()
This article Hariyanto Lim 探索了替代排序方法,似乎有几种信誉良好的自定义排序算法可供您选择甚至构建。
从他的比较来看,最快的似乎是 Chrome 和 Safari 中的 QuickInsertionSort
,而 Firefox 中的其他任何一个 quickSort
函数,其中 QuickInsertionSort
奇怪在某些情况下变得和原生 JS sort()
方法一样慢。
以下是探索的所有三个替代函数的源代码:
QuickInsertionSort()
function QuickInsertionSort(arr) {
'use strict';
if(!arr || 1 > arr.length) {
return null;
}
var startIndex = 0, endIndex = arr.length - 1;
// use 'stack' data structure to eliminate recursive call
// DON'T use Array.push() and Array.pop() because slow !!!
// so use manual indexing
var stackLength = 0;
// use 2 arrays instead of 1 array to fasten (reduce calculation of '+= 2' and '-= 2')
var startIndexes = [];
var endIndexes = [];
// variables for partitioning
var partitionIndex, pivot, left, right, _swap_temp;
// variables for insertion sort
var i, j, key;
do {
// in my testing, I found 32 is very good choice for totally generated-random data,
// more than 100 will cause slower speed overal.
if(32 >= endIndex - startIndex) {
// even using insertionSort,
// still need this because it still come here !!
if(1 == endIndex - startIndex) {
if(arr[startIndex] > arr[endIndex]) {
_swap_temp = arr[startIndex];
arr[startIndex] = arr[endIndex];
arr[endIndex] = _swap_temp;
}
} else {
/**************************************
****** start of insertion sort ********
***************************************/
for(i = startIndex + 1; endIndex >= i; i++) {
key = arr[i];
// Move elements of arr[startIndex..i-1], that are
// greater than key, to one position ahead
// of their current position
for (j = i - 1; j >= startIndex; j--) {
if(arr[j] > key) {
arr[j + 1] = arr[j];
continue;
}
// use 'break' to avoid decreasing 'j'
break;
}
// swap
arr[j + 1] = key;
}
/**************************************
****** end of insertion sort **********
***************************************/
}
// continue to process next data, is there any data inside stack ?
if(stackLength > 0) {
// pop
stackLength--; // reduce counter to get the last position from stack
startIndex = startIndexes[stackLength];
endIndex = endIndexes[stackLength];
} else {
// no data inside stack, so finish
break;
}
} else {
// squeeze every millisecond by put main logic here instead of separate function
// in my testing using median_of_3 does not give better result for generated totally random data !!
/*********************************************
*********** start of partitioning ************
************* Tony Hoare *********************
**********************************************/
// minimize worst case scenario
// === start of select pivot ============
pivot = arr[startIndex];
// try to find a different element value
j = endIndex;
while(pivot == arr[j] && j >= startIndex) {
j--;
}
if(j > startIndex) {
// check which element is lower?
// use the lower value as pivot
if(pivot > arr[j]) {
pivot = arr[j];
}
}
// === end of select pivot ============
left = startIndex;
right = endIndex;
do {
while(pivot > arr[left]) {
left++;
}
while(arr[right] > pivot) {
right--;
}
if(left >= right) {
partitionIndex = right;
break;
}
//swap(left, right);
// because many swaps, so optimize to implement swap here !
_swap_temp = arr[left];
arr[left] = arr[right];
arr[right] = _swap_temp;
left++;
right--;
} while(true); // loop forever until break
if(partitionIndex > startIndex) {
// has lower partition, so process it
if(endIndex > partitionIndex + 1) {
// push 'right' side partition info into stack for later
startIndexes[stackLength] = partitionIndex + 1;
endIndexes[stackLength] = endIndex;
stackLength++; // increase counter for NEXT slot
}
// prepare next loop
// keep same value for startIndex but update endIndex
endIndex = partitionIndex;
} else if(endIndex > partitionIndex + 1) {
// at this point, it means there is no 'lower' side partition but has 'higher' side partition
// prepare next loop
// keep same value for endIndex but update startIndex
startIndex = partitionIndex + 1;
}
/*********************************************
****** end of Tony Hoare partitioning ********
**********************************************/
}
} while(endIndex > startIndex);
}
quickSort_by_Tony_Hoare_non_recursive()
function quickSort_by_Tony_Hoare_non_recursive(arr) {
'use strict';
if(!arr || 1 > arr.length) {
return null;
}
var arrLength = arr.length;
var startIndex = 0, endIndex = arrLength - 1;
// don't use Array.push() and Array.pop() because too slow
// use 2 arrays instead of 1 to avoid unnecessary increasing and reducing stackLength
var stackStartIndex = [], stackEndIndex = [];
var stackLength = 0;
var partitionIndex;
var i, j, is_key;
do {
partitionIndex = partition_by_Tony_Hoare(arr, startIndex, endIndex);
if(partitionIndex > startIndex) {
// there is lower values to partition
// is there higher values?
if(endIndex > partitionIndex + 1) {
// we don't do it now, push it into stack for later
stackStartIndex[stackLength] = partitionIndex + 1;
stackEndIndex[stackLength] = endIndex;
stackLength++; // increase counter for next slot
}
// set new parameter to partition lower values
endIndex = partitionIndex;
} else if(endIndex > partitionIndex + 1) {
// there is no lower values, only higher value, this is worst case!
// set new parameter for next partitioning
startIndex = partitionIndex + 1;
} else {
// no valid partitioning index, so we get from stack (if any)
if(stackLength > 0) {
stackLength--;
startIndex = stackStartIndex[stackLength];
endIndex = stackEndIndex[stackLength];
} else {
break; // finished !
}
}
} while(endIndex > startIndex);
return arr;
}
quickSort_by_Nico_Lomuto()
function quickSort_by_Nico_Lomuto(arr, startIndex, endIndex) {
// using Nico Lomuto partition scheme
// simpler and easier to understand.
if(endIndex > startIndex) {
var partitionIndex = partition_by_Nico_Lomuto(arr, startIndex, endIndex);
// the item at partitionIndex will not be included in recursive sorting because
// arr[partitionIndex] >= [...lowers]
// [...highers] >= arr[partitionIndex]
// recursion to sort lower values
quickSort_by_Nico_Lomuto(arr, startIndex, partitionIndex - 1);
// recursion to sort higher values
quickSort_by_Nico_Lomuto(arr, partitionIndex + 1, endIndex);
}
return arr;
}
function partition_by_Nico_Lomuto(arr, startIndex, endIndex) {
// easier to implement and understand
//var pivot = arr[startIndex];
// Lomuto partitioning has worst case if selected pivot value is LARGEST value in the range!
// prevent worst case by carefully selecting pivot value!
var pivot = selectPivot(arr, startIndex, endIndex, true); // true = MUST do swapping !
var i = startIndex;
// one time loop from bottom to the second from top, because pivot is the top position
for(j = startIndex; endIndex > j; j++) {
// is current element is smaller than or equal to pivot ?
if(pivot >= arr[j]) {
// swap
swap(arr, i, j);
i++;
}
}
// swap
swap(arr, i, endIndex);
return i;
}
function selectPivot(arr, startIndex, endIndex, doSwap) {
// find a pivot value which not the lowest value within the range
// Get 2 UNIQUE elements, if failed then it means all elements are same value.
var pivot = arr[startIndex]; // get first element from the first position
// try to find a different element value
var j = endIndex;
while(pivot == arr[j] && j >= startIndex) {
j--;
}
if(startIndex > j) {
//console.log('selectPivot(arr, ' + startIndex + ',' + endIndex + '), all elements are equal, nothing to sort');
return pivot;
}
// check which element is lower?
// use the lower value as pivot and swap the position with the last position (endIndex)
if(pivot > arr[j]) {
pivot = arr[j];
if(doSwap) {
swap(arr, j, endIndex);
}
} else {
if(doSwap) {
swap(arr, startIndex, endIndex);
}
}
return pivot;
}
function swap(arr, a, b) {
// replace more than 1 element value in array using 1 line
// this ability is 'ES6 destructuring swap',
// only specific for Javascript language
// but VERY VERY SLOW, almost 3 times slower !
//[arr[a], arr[b]] = [arr[b], arr[a]];
// normal way for many programming language
var _swap_temp = arr[a];
arr[a] = arr[b];
arr[b] = _swap_temp;
}
关于javascript - 根据嵌套键值对对象数组进行排序的最快方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67285732/
我喜欢 smartcase,也喜欢 * 和 # 搜索命令。但我更希望 * 和 # 搜索命令区分大小写,而/和 ?搜索命令遵循 smartcase 启发式。 是否有隐藏在某个地方我还没有找到的设置?我宁
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题? Update the question所以它是on-topic对于堆栈溢出。 10年前关闭。 Improve this qu
从以下网站,我找到了执行java AD身份验证的代码。 http://java2db.com/jndi-ldap-programming/solution-to-sslhandshakeexcepti
似乎 melt 会使用 id 列和堆叠的测量变量 reshape 您的数据框,然后通过转换让您执行聚合。 ddply,从 plyr 包看起来非常相似..你给它一个数据框,几个用于分组的列变量和一个聚合
我的问题是关于 memcached。 Facebook 使用 memcached 作为其结构化数据的缓存,以减少用户的延迟。他们在 Linux 上使用 UDP 优化了 memcached 的性能。 h
在 Camel route ,我正在使用 exec 组件通过 grep 进行 curl ,但使用 ${HOSTNAME} 的 grep 无法正常工作,下面是我的 Camel 路线。请在这方面寻求帮助。
我正在尝试执行相当复杂的查询,在其中我可以排除与特定条件集匹配的项目。这是一个 super 简化的模型来解释我的困境: class Thing(models.Model) user = mod
我正在尝试执行相当复杂的查询,我可以在其中排除符合特定条件集的项目。这里有一个 super 简化的模型来解释我的困境: class Thing(models.Model) user = mod
我发现了很多嵌入/内容项目的旧方法,并且我遵循了在这里找到的最新方法(我假设):https://blog.angular-university.io/angular-ng-content/ 我正在尝试
我正在寻找如何使用 fastify-nextjs 启动 fastify-cli 的建议 我曾尝试将代码简单地添加到建议的位置,但它不起作用。 'use strict' const path = req
我正在尝试将振幅 js 与 React 和 Gatsby 集成。做 gatsby developer 时一切看起来都不错,因为它发生在浏览器中,但是当我尝试 gatsby build 时,我收到以下错
我试图避免过度执行空值检查,但同时我想在需要使代码健壮的时候进行空值检查。但有时我觉得它开始变得如此防御,因为我没有实现 API。然后我避免了一些空检查,但是当我开始单元测试时,它开始总是等待运行时异
尝试进行包含一些 NOT 的 Kibana 搜索,但获得包含 NOT 的结果,因此猜测我的语法不正确: "chocolate" AND "milk" AND NOT "cow" AND NOT "tr
我正在使用开源代码共享包在 iOS 中进行 facebook 集成,但收到错误“FT_Load_Glyph failed: glyph 65535: error 6”。我在另一台 mac 机器上尝试了
我正在尝试估计一个标准的 tobit 模型,该模型被审查为零。 变量是 因变量 : 幸福 自变量 : 城市(芝加哥,纽约), 性别(男,女), 就业(0=失业,1=就业), 工作类型(失业,蓝色,白色
我有一个像这样的项目布局 样本/ 一种/ 源/ 主要的/ java / java 资源/ .jpg 乙/ 源/ 主要的/ java / B.java 资源/ B.jpg 构建.gradle 设置.gr
如何循环遍历数组中的多个属性以及如何使用map函数将数组中的多个属性显示到网页 import React, { Component } from 'react'; import './App.css'
我有一个 JavaScript 函数,它进行 AJAX 调用以返回一些数据,该调用是在选择列表更改事件上触发的。 我尝试了多种方法来在等待时显示加载程序,因为它当前暂停了选择列表,从客户的 Angul
可能以前问过,但找不到。 我正在用以下形式写很多语句: if (bar.getFoo() != null) { this.foo = bar.getFoo(); } 我想到了三元运算符,但我认
我有一个表单,在将其发送到 PHP 之前我正在执行一些验证 JavaScript,验证后的 JavaScript 函数会发布用户在 中输入的文本。页面底部的标签;然而,此消息显示短暂,然后消失...
我是一名优秀的程序员,十分优秀!