英文:
javascript : Sort a subset of an array (created by a filter)
问题
我试图对数组的一个子集进行排序,该子集是数组的筛选版本,我需要整个数组(带有排序的子集)作为结果。
这是主要数组:
var data = [
{ name: "Cccc", sortCriteria: 1 },
{ name: "Bbbb", sortCriteria: 1 },
{ name: "Dddd", sortCriteria: 0 },
{ name: "Eeee", sortCriteria: 1 },
{ name: "Ffff", sortCriteria: 0 },
];
排序标准用于筛选数组:
var data2 = data.filter(function (attribute) {
return attribute.sortCriteria == 1;
});
最后,我对筛选后的数组进行排序:
var data3 = data2.sort(function (a, b) {
var aname = a.name.toLowerCase();
var bname = b.name.toLowerCase();
if (aname < bname) {
return -1;
}
if (aname > bname) {
return 1;
}
});
每个步骤都有效,但data2和data3只是子集(我需要整个数组),而data显然没有排序。
我的结果应该是:
Bbbb
Cccc
Dddd
Eeee
Ffff
有没有办法使用ecmascript 5来实现这个(顺序可以是随机的,这是一个示例)?
编辑:以下问题没有解决我的问题,因为它不符合ecma5的规范。
英文:
I'm trying to sort only a subset of an array, the subset is a filtered version of that array, and I need the whole array (with the subset sorted ) as a result.
Here is the main array :
var data = [
{ name: "Cccc", sortCriteria: 1 },
{ name: "Bbbb", sortCriteria: 1 },
{ name: "Dddd", sortCriteria: 0 },
{ name: "Eeee", sortCriteria: 1 },
{ name: "Ffff", sortCriteria: 0 },
];
The sort criteria is used to filter the array :
var data2 = data.filter(function (attribute) {
return attribute.sortCriteria == 1;
});
Finally i'm sorting the filtered array :
var data3 = data2.sort(function (a, b) {
var aname = a.name.toLowerCase();
var bname = b.name.toLowerCase();
if (aname < bname) {
return -1;
}
if (aname > bname) {
return 1;
}
});
Every step works but data2 & data3 are subset only (i need the wole array) and data is obviously not sorted.
My result should be :
Bbbb
Cccc
Dddd
Eeee
Ffff
Any idea how that can be done using ecmascript 5 (the order can be random, this is a example) ?
Edit : the following question doesnt address my issue because it's not ecma5 compliant.
答案1
得分: 2
var data = [{ name: "Cccc", sortCriteria: 1 }, { name: "Bbbb", sortCriteria: 1 }, { name: "Dddd", sortCriteria: 0 }, { name: "Eeee", sortCriteria: 1 }, { name: "Ffff", sortCriteria: 0 }],
data2 = data.filter(function (item) {
return item.sortCriteria === 1;
}),
indices = data2
.map(function (_, i) {
return i;
})
.sort(function (a, b) {
var aname = data2[a].name.toLowerCase(),
bname = data2[b].name.toLowerCase();
if (aname < bname) return -1;
if (aname > bname) return 1;
return 0;
}),
index = 0,
sorted = data.map(function (item) {
return item.sortCriteria === 1
? data2[indices[index++]]
: item;
});
console.log(sorted);
英文:
You could get an array of the items fro sorting and their indices and sort the indices an map a new array with either not sorted items or sorted items.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
var data = [{ name: "Cccc", sortCriteria: 1 }, { name: "Bbbb", sortCriteria: 1 }, { name: "Dddd", sortCriteria: 0 }, { name: "Eeee", sortCriteria: 1 }, { name: "Ffff", sortCriteria: 0 }],
data2 = data.filter(function (item) {
return item.sortCriteria === 1;
}),
indices = data2
.map(function (_, i) {
return i;
})
.sort(function (a, b) {
var aname = data2[a].name.toLowerCase(),
bname = data2[b].name.toLowerCase();
if (aname < bname) return -1;
if (aname > bname) return 1;
return 0;
}),
index = 0,
sorted = data.map(function (item) {
return item.sortCriteria === 1
? data2[indices[index++]]
: item;
});
console.log(sorted);
<!-- language: lang-css -->
.as-console-wrapper { max-height: 100% !important; top: 0; }
<!-- end snippet -->
答案2
得分: 1
以下是翻译好的部分:
首先,您的测试数据存在问题。如果数据排序没有考虑sortCriteria,结果将是相同的。因此,我已经更改了测试数据,以使其更明显。
我们记住了带有sortCriteria的项目的索引,对它们进行排序,然后将它们推回到数据中的记住的索引位置。
注意:将Cccc
的sortCriteria更改为0
,以便清楚它没有排序,并保留在索引0
,还将Bbbb
移动到最后,以确保在排序之后出现在Eeee
之前。
以下是示例代码:
var data = [{name: "Cccc", sortCriteria: 0},
{name: "Dddd", sortCriteria: 0},
{name: "Eeee", sortCriteria: 1},
{name: "Ffff", sortCriteria: 0},
{name: "Bbbb", sortCriteria: 1}];
var items = [], idxs = [];
for (var j = 0; j < data.length; j++) {
const item = data[j];
if (item.sortCriteria === 1) {
// 收集具有sortCriteria === 1的项目、其索引和排序值
items.push([item, item.name.toLowerCase()]);
idxs.push(j);
}
}
// 按排序值进行排序
items.sort(function (a, b) {
return a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0;
});
// 使用收集的索引将排序后的项目放回数据数组中
let idx = 0;
for (var j = 0; j < items.length; j++) {
data[idxs[idx++]] = items[j][0];
}
// 仅用于日志记录,不是es2015
console.log(data.map(({ name }) => name));
还有一个涉及到测试数据的性能测试。
希望这可以帮助您理解代码的内容。
英文:
First you have problem with your test data. If it's sorted without even sortCriteria in mind the result will be the same. So I've changed the test data to make it more obvious.
We remember indices of items with sortCriteria, sort them and push back to data at remembered indices.
NOTICE: changed Cccc
's sortCriteria to 0
so it would be clear that it's not sorted and stays at index 0
, also Bbbb
moved to the end so it should appear before `Eeee' after the sorting:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
var data = [{name : "Cccc", sortCriteria : 0},
{name : "Dddd", sortCriteria : 0}, {name : "Eeee", sortCriteria : 1}, {name : "Ffff", sortCriteria : 0}, {name : "Bbbb", sortCriteria : 1}];
var items = [], idxs =[];
for(var j = 0; j < data.length; j++){
const item = data[j];
if(item.sortCriteria === 1){
// colect item, its index and sort value with sortCriteria === 1
items.push([item, item.name.toLowerCase()]);
idxs.push(j);
}
}
// sort by the sort value
items.sort(function(a, b){
return a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0;
});
// put sorted items back into the data array using collected indices
let idx = 0;
for(var j = 0; j < items.length; j++){
data[idxs[idx++]] = items[j][0];
}
//not es2015, just logging
console.log(data.map(({name})=>name));
<!-- end snippet -->
And a benchmark with test data x 1000000.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-html -->
<script benchmark data-count="1">
var chunk = [{name : "Cccc", sortCriteria : 0},
{name : "Dddd", sortCriteria : 0}, {name : "Eeee", sortCriteria : 1}, {name : "Ffff", sortCriteria : 0}, {name : "Bbbb", sortCriteria : 1}];
var data = [];
for (let j = 0; j < 1000000; j++) {
data.push(...chunk);
}
// @benchmark Alexander's solution
var items = [], idxs =[];
for(var j = 0; j < data.length; j++){
const item = data[j];
if(item.sortCriteria === 1){
// colect item, its index and sort value with sortCriteria === 1
items.push([item, item.name.toLowerCase()]);
idxs.push(j);
}
}
// sort by the sort value
items.sort(function(a, b){
return a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0;
});
// put sorted items back into the data array using collected indices
let idx = 0;
for(var j = 0; j < items.length; j++){
data[idxs[idx++]] = items[j][0];
}
data;
// @benchmark Nina's solution
var data2 = data.filter(function (item) {
return item.sortCriteria === 1;
}),
indices = data2
.map(function (_, i) {
return i;
})
.sort(function (a, b) {
var aname = data2[a].name.toLowerCase(),
bname = data2[b].name.toLowerCase();
if (aname < bname) return -1;
if (aname > bname) return 1;
return 0;
}),
index = 0,
sorted = data.map(function (item) {
return item.sortCriteria === 1
? data2[indices[index++]]
: item;
});
sorted;
</script>
<script src="https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js"></script>
<!-- end snippet -->
答案3
得分: 0
只需在排序函数的开头添加 if (!a.sortCriteria || !b.sortCriteria) return 0;
即可避免对具有 sortCriteria 0 的元素进行排序。(Array.prototype.sort() 在原地排序,因此无需 var data2 = data.sort(...)
- 这里的 data 和 data2 是同一个数组)。
var data = [
{ name: "Cccc", sortCriteria: 1 },
{ name: "Bbbb", sortCriteria: 1 },
{ name: "Dddd", sortCriteria: 0 },
{ name: "Eeee", sortCriteria: 1 },
{ name: "Ffff", sortCriteria: 0 },
];
data.sort(function(a, b) {
if (!a.sortCriteria || !b.sortCriteria) return 0;
var aname = a.name.toLowerCase();
var bname = b.name.toLowerCase();
if (aname < bname) return -1;
if (aname > bname) return 1;
return 0;
});
console.log(data);
英文:
Just add if (!a.sortCriteria || !b.sortCriteria) return 0;
at the beginning of the sorting function to avoid sorting elements with sortCriteria 0. (Array.prototype.sort() sorts in-place, so there is no need to var data2 = data.sort(...)
- data and data2 is the same array here.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
var data = [
{ name: "Cccc", sortCriteria: 1 },
{ name: "Bbbb", sortCriteria: 1 },
{ name: "Dddd", sortCriteria: 0 },
{ name: "Eeee", sortCriteria: 1 },
{ name: "Ffff", sortCriteria: 0 },
];
data.sort(function(a, b) {
if (!a.sortCriteria || !b.sortCriteria) return 0;
var aname = a.name.toLowerCase();
var bname = b.name.toLowerCase();
if (aname < bname) return -1;
if (aname > bname) return 1;
return 0;
});
console.log(data);
<!-- end snippet -->
答案4
得分: -1
你可以在手动筛选时存储每个元素的原始索引,然后使用这些索引来更新原始数组中对象的位置。
var data = [{name: "Cccc", sortCriteria: 1}, {name: "Bbbb", sortCriteria: 1}, {name: "Dddd", sortCriteria: 0}, {name: "Eeee", sortCriteria: 1}, {name: "Ffff", sortCriteria: 0}];
var filtered = [], origIdx = [];
for (var i = 0; i < data.length; i++)
if (data[i].sortCriteria === 1)
filtered.push(data[i]), origIdx.push(i);
filtered.sort(function(a, b){return a.name.toLowerCase().localeCompare(b.name.toLowerCase())});
for (var i = 0; i < filtered.length; i++)
data[origIdx[i]] = filtered[i];
console.log(data);
希望对你有所帮助。
英文:
You can store the original index of each element while filtering manually, then use those indexes to update the positions of the objects back in the original array.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
var data = [{name : "Cccc", sortCriteria : 1}, {name : "Bbbb", sortCriteria : 1}, {name : "Dddd", sortCriteria : 0}, {name : "Eeee", sortCriteria : 1}, {name : "Ffff", sortCriteria : 0}];
var filtered = [], origIdx = [];
for (var i = 0; i < data.length; i++)
if (data[i].sortCriteria === 1)
filtered.push(data[i]), origIdx.push(i);
filtered.sort(function(a, b){return a.name.toLowerCase().localeCompare(b.name.toLowerCase())});
for (var i = 0; i < filtered.length; i++)
data[origIdx[i]] = filtered[i];
console.log(data);
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论