如何防止对象数字属性的自动排序?

Posted

技术标签:

【中文标题】如何防止对象数字属性的自动排序?【英文标题】:How to prevent automatic sort of Object numeric property? 【发布时间】:2016-01-25 21:51:09 【问题描述】:

我遇到这个问题的原因: 我试图解决一个算法问题,我需要返回数组中出现次数最多的数字。像 [5,4,3,2,1,1] 应该返回 1。 而且,当两个数字与最大出现次数同时出现时,第一个出现。像 [5,5,2,2,1] 返回 5 因为 5 首先出现。我使用一个对象来存储每个数字的外观。关键是数字本身。

所以当输入是 [5,5,2,2,1] 我的对象应该是 Object 5: 2, 2: 2, 1: 1 但实际上我得到了Object 1: 1, 2: 2, 5: 2 因此,当我使用 for..in 迭代对象时,我得到了 2 而不是 5 返回。所以这就是我问这个问题的原因。

此问题出现在 Chrome 控制台中,我不确定这是否是常见问题: 当我运行以下代码时

var a = ;
a[0]=1;
a[1]=2;
a[2]=3;

a 是:Object 0: 1, 1: 2, 2: 3

但是当我颠倒分配顺序时:

 var a = ;
 a[2]=3;
 a[1]=2;
 a[0]=1;

a 也是:Object 0: 1, 1: 2, 2: 3 数值属性自动按升序排序。 我尝试为数字属性添加前缀或后缀,例如

var a = ;
a['p'+0]=1;
a['p'+1]=2;
a['p'+2]=3;
console.log(a);//Object p0: 1, p1: 2, p2: 3

这保持了财产秩序。这是解决问题的最佳方法吗?有没有办法阻止这种自动排序行为?这只发生在 Chrome V8 javascript 引擎中吗?提前谢谢!

【问题讨论】:

为什么要使用对象? 对象本质上是无序的键值对。他们没有订单,所以不要指望他们会保留订单。您的实际问题是什么? 见编辑的家伙。很抱歉造成混乱。 @TinyGiant 感谢您的代码。但是如果我想找到最先出现的模式呢?在您的代码中,如果输入是 [5,5,4,3,2,1,1],我怎样才能得到 5 而不是 1? 在您的回答中,您告诉在键中使用字符串可以保持顺序不变,但在您的示例中,您只显示了有序分配。您是否也尝试过使用字符串进行无序赋值? 【参考方案1】:

您正在使用 JS object,根据定义,它不会保持秩序。将其视为键 => 值映射。

您应该使用array,它将保留您在index 上插入的任何内容。把它想象成一个列表。

还请注意,您实际上没有“颠倒了赋值的顺序”,因为您每次都在相同的索引上插入元素。

【讨论】:

是的,我知道这一点。好像我没有清楚地解释我的问题......我已经编辑了我的问题。 @AndyHu 您的问题仍然存在:您正试图在object 中保持秩序,而这只是not 保持秩序。您可能需要更好的数据结构来解决您遇到的其他问题,使用数组进行排序,也可能使用对象进行计数。 好的,我会尝试另一种方式。谢谢! 答案已经过时了。自 ES2015 以来,JS 对象已经对键进行了排序(是的,按规范)。只是数字键的行为不同。【参考方案2】:

而不是生成像5: 2, 2: 2, 1: 1这样的对象

生成一个数组的效果

[
   key: 5, val: 2,
   key: 2, val: 2,
   key: 1, val: 1
]

或...在单独的值或键中跟踪排序顺序

【讨论】:

【参考方案3】:
target = 
target[' ' + key] = value // numeric key

这可以防止对象数字属性的自动排序。

【讨论】:

这与@AndyHu 建议使用前缀相同。不同之处在于,在您的情况下,它是带有空格而不是字母的前缀。【参考方案4】:

只需在使用数字数组索引时执行此操作

data      = 
data[key] = value

【讨论】:

【参考方案5】:

您确实不能依赖 JavaScript 中对象字段的顺序,但如果您需要,我可以建议使用 MapES6/ES2015 标准)保留您的 key, value 对对象的顺序。请参阅下面的 sn-p:

let myObject = new Map();
myObject.set('z', 33);
myObject.set('1', 100);
myObject.set('b', 3);

for (let [key, value] of myObject) 
  console.log(key, value);

// z 33
// 1 100
// b 3

【讨论】:

【参考方案6】:

我遇到了同样的问题,经过大量搜索后,我发现防止这种行为的解决方案是将密钥作为字符串。

这样:

"a": 2, "b": 2

【讨论】:

是的,除了数字的字符串值外,键应该是字符串。如果你做类似 let obj = , obj["5"] = 2; obj["2"] = 2,键顺序仍然是 2,5,这不是您想要的。必须使用一些前缀。【参考方案7】:

我在使用 Ids 键的规范化数组时偶然发现了这个问题> 经过研究,我发现无法使用对象键修复,因为默认情况下,当您使用数字对任何对象键进行排序时迭代它。

我已经完成并且对我有用的解决方案是放置一个“sortIndex”字段并使用它对列表进行排序。

【讨论】:

【参考方案8】:

这是一个古老的话题,但仍然值得一提,因为在一分钟的谷歌搜索中很难找到直接的解释。

我最近有一个编码练习,在数组中找到最少/最多 频繁整数的第一次出现,这几乎是和你的情况一样。

我遇到了和你一样的问题,JavaScript对象中的数字键按ASC排序,不保留元素的原始顺序,这是js中的默认行为。

在 ES6 中解决这个问题的更好方法是使用一种新的数据类型,称为:Map

Map 可以保留元素(对)的原始顺序,并且还具有对象的唯一键优势。

let map = new Map()
map.set(4, "first") // Map(1) 4 => "first"
map.set(1, "second") // Map(2) 4 => "first", 1 => "second"
map.set(2, "third") // Map(3) 4 => "first", 1 => "second", 2 => "third"
for(let [key, value] of map) 
  console.log(key, value)

// 4 "first"
// 1 "second"
// 2 "third"

不过,使用object数据类型也可以解决问题,但是我们需要借助输入数组来找回元素原来的顺序:

function findMostAndLeast(arr) 
  let countsMap = ;
  let mostFreq = 0;
  let leastFreq = arr.length;
  let mostFreqEl, leastFreqEl;

  for (let i = 0; i < arr.length; i++) 
    let el = arr[i];
    // Count each occurrence
    if (countsMap[el] === undefined) 
      countsMap[el] = 1;
     else 
      countsMap[el] += 1;
    
  

  // Since the object is sorted by keys by default in JS, have to loop again the original array
  for (let i = 0; i < arr.length; i++) 
    const el = arr[i];

    // find the least frequent 
    if (leastFreq > countsMap[el]) 
      leastFreqEl = Number(el);
      leastFreq = countsMap[el];
    

    // find the most frequent 
    if (countsMap[el] > mostFreq) 
      mostFreqEl = Number(el);
      mostFreq = countsMap[el];
    
  

  return 
    most_frequent: mostFreqEl,
    least_frequent: leastFreqEl
  

const testData = [6, 1, 3, 2, 4, 7, 8, 9, 10, 4, 4, 4, 10, 1, 1, 1, 1, 6, 6, 6, 6];    
console.log(findMostAndLeast(testData)); //  most_frequent: 6, least_frequent: 3 , it gets 6, 3 instead of 1, 2 

【讨论】:

【参考方案9】:

    您可以在 javascript ES6 中使用 Map() 来保持键插入的顺序。

    只是想用另一种方法解决你的问题,最近喜欢练习类似leetcode的问题

function solution(arr) 
  const obj = ;
  const record = 
    value: null,
    count: 0
  ;

  for (let i = 0; i < arr.length; i++) 
    let current = arr[i];
    if (!obj[current]) 
      obj[current] = 0;
    

    obj[current]++;

    if (obj[current] > record.count) 
      record.value = current;
      record.count = obj[current];
    
  

  console.log("mode number: ", record.value);
  console.log("mode number count: ", record.count);

【讨论】:

【参考方案10】:

Object.keys()得到的数组中保持键的顺序最简单也是最好的方法就是稍微操作一下Object键。

在每个键名前插入一个“_”。然后运行以下代码!

myObject = 
  _a: 1,
  _1: 2,
  _2: 3


const myObjectRawKeysArray = Object.keys(myObject); 
console.log(myObjectRawKeysArray)
//["_a", "_1", "_2"]

const myDesiredKeysArray = myObjectRawKeysArray.map(rawKey => return rawKey.slice(1)); 
console.log(myDesiredKeysArray)
//["a", "1", "2"]

您只需几行代码即可在数组中获得所需的顺序。快乐编码:)

【讨论】:

【参考方案11】:

为了防止 Javascript 中 Object 的数字键自动排序,最好的方法是稍微调整一下 Object 键。

我们可以在每个键名前插入一个“e”,以避免对键进行字典排序,并使用以下代码获得正确的输出切片“e”;

object_1 = 
      "3": 11,
      "2": 12,
      "1": 13
      

let automaticSortedKeys = Object.keys(object_1);
console.log(automaticSortedKeys)     //["1", "2", "3"]

object_2 = 
      "e3": 11,
      "e2": 12,
      "e1": 13
      
    
let rawObjectKeys = Object.keys(object_2); 
console.log(rawObjectKeys)   //["e3", "e2", "e1"]
    
let properKeys = rawObjectKeys.map(function(element)
      return element.slice(1)
      ); 
console.log(properKeys)     //["3", "2", "1"]

【讨论】:

以上是关于如何防止对象数字属性的自动排序?的主要内容,如果未能解决你的问题,请参考以下文章

如何在我的递归快速排序算法中防止堆栈溢出

如何防止ag-grid内联单元格编辑自动修改行数据

防止在可排序的 JqueryUI 中删除列表项

防止手机浏览点击输入框自动放大

使用Python SDK时如何防止GCS自动解压对象?

MFC CListBox AddString - 防止自动排序