JavaScript:对数组的子集进行排序(由筛选器创建)

huangapple go评论77阅读模式
英文:

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的规范。

https://stackoverflow.com/questions/65561842/javascript-sort-the-elements-of-the-array-without-changing-positions-of-certai

英文:

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: &quot;Cccc&quot;, sortCriteria: 1 },
  { name: &quot;Bbbb&quot;, sortCriteria: 1 },
  { name: &quot;Dddd&quot;, sortCriteria: 0 },
  { name: &quot;Eeee&quot;, sortCriteria: 1 },
  { name: &quot;Ffff&quot;, 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 &lt; bname) {
    return -1;
  }
  if (aname &gt; 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.

https://stackoverflow.com/questions/65561842/javascript-sort-the-elements-of-the-array-without-changing-positions-of-certai

答案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 &lt; bname) return -1;
            if (aname &gt; 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 : &quot;Cccc&quot;, sortCriteria : 0},  
{name : &quot;Dddd&quot;, sortCriteria : 0}, {name : &quot;Eeee&quot;, sortCriteria : 1}, {name : &quot;Ffff&quot;, sortCriteria : 0}, {name : &quot;Bbbb&quot;, sortCriteria : 1}];

var items = [], idxs =[];
for(var j = 0; j &lt; 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] &gt; b[1] ? 1 : a[1] &lt; b[1] ? -1 : 0;
});
// put sorted items back into the data array using collected indices
let idx = 0;
for(var j = 0; j &lt; items.length; j++){
  data[idxs[idx++]] = items[j][0];
}

//not es2015, just logging
console.log(data.map(({name})=&gt;name));

<!-- end snippet -->

And a benchmark with test data x 1000000.

JavaScript:对数组的子集进行排序(由筛选器创建)

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-html -->

&lt;script benchmark data-count=&quot;1&quot;&gt;

    var chunk = [{name : &quot;Cccc&quot;, sortCriteria : 0},  
{name : &quot;Dddd&quot;, sortCriteria : 0}, {name : &quot;Eeee&quot;, sortCriteria : 1}, {name : &quot;Ffff&quot;, sortCriteria : 0}, {name : &quot;Bbbb&quot;, sortCriteria : 1}];
    var data = [];
    for (let j = 0; j &lt; 1000000; j++) {
        data.push(...chunk);
    }
    
    // @benchmark Alexander&#39;s solution

    var items = [], idxs =[];
    for(var j = 0; j &lt; 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] &gt; b[1] ? 1 : a[1] &lt; b[1] ? -1 : 0;
    });
    // put sorted items back into the data array using collected indices
    let idx = 0;
    for(var j = 0; j &lt; items.length; j++){
      data[idxs[idx++]] = items[j][0];
    }

    data;    

    // @benchmark Nina&#39;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 &lt; bname) return -1;
                if (aname &gt; bname) return 1;
                return 0;
            }),
            index = 0,
        sorted = data.map(function (item) {
            return item.sortCriteria === 1
                ? data2[indices[index++]]
                : item;
        });
    sorted;    



&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js&quot;&gt;&lt;/script&gt;

<!-- 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: &quot;Cccc&quot;, sortCriteria: 1 },
  { name: &quot;Bbbb&quot;, sortCriteria: 1 },
  { name: &quot;Dddd&quot;, sortCriteria: 0 },
  { name: &quot;Eeee&quot;, sortCriteria: 1 },
  { name: &quot;Ffff&quot;, 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 &lt; bname) return -1;
    if (aname &gt; 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 : &quot;Cccc&quot;, sortCriteria : 1}, {name : &quot;Bbbb&quot;, sortCriteria : 1}, {name : &quot;Dddd&quot;, sortCriteria : 0}, {name : &quot;Eeee&quot;, sortCriteria : 1}, {name : &quot;Ffff&quot;, sortCriteria : 0}];
var filtered = [], origIdx = [];
for (var i = 0; i &lt; 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 &lt; filtered.length; i++)
  data[origIdx[i]] = filtered[i];
console.log(data);

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年6月26日 00:21:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/76551387.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定