如何在Javascript中对对象进行切片?

46

我试图使用Array.prototype来切割一个对象,但它返回一个空数组,除了传递参数之外,是否有任何方法可以切割对象,或者只是我的代码出了问题?谢谢!!

var my_object = {
 0: 'zero',
 1: 'one',
 2: 'two',
 3: 'three',
 4: 'four'
};

var sliced = Array.prototype.slice.call(my_object, 4);
console.log(sliced);

你能做的最接近的方法是使用 delete object.property 删除键,并确保在此之前将该属性存储在单独的变量中。 - Jeremy Jackson
你期望的输出是什么?对一个对象进行切片没有意义 - 它没有任何含义,但如果你能解释一下你期望的输出是什么,我们可以努力实现。 - winhowes
我尝试创建一个新的数组,只包含我需要的属性,例如:var obj = {car: 'audi', engine: 'diesel', color: 'blue'}作为输出,我想要一个只包含“color”属性的新对象。 - JC Garcia
我想要一个新的对象 - 但是 Array.slice 返回一个数组。 - RomanPerekhrest
11个回答

71

还没有人提到Object.entries(),这可能是最灵活的方法。该方法在枚举属性时使用与for..in相同的顺序,即属性最初输入对象的顺序。您还可以获得包含属性和值的子数组,因此您可以使用其中任何一个或两个都可以。最后,您不必担心属性是否为数字或设置额外的长度属性(当使用Array.prototype.slice.call()时需要)。
以下是一个示例:

const obj = {'prop1': 'foo', 'prop2': 'bar', 'prop3': 'baz', 'prop4': {'prop': 'buzz'}};

你想切片前两个值:

Object.entries(obj).slice(0,2).map(entry => entry[1]);
//["foo", "bar"]

所有的键?

Object.entries(obj).slice(0).map(entry => entry[0]);
//["prop1", "prop2", "prop3", "prop4"]

最后一个键值对?
Object.entries(obj).slice(-1)
//[ ['prop4', {'prop': 'buzz'}] ]

9
Object.entries 是一个非常有用的方法(显然是在 ECMA 262 中新增的)……但它似乎只会生成一个数组的数组,这并不是 OP 所询问的内容。 - mike rodent
1
Object.entries在IE中不受支持。 - MBouwman
3
很遗憾,使用这种方法会产生一个新的数组而不是一个新的对象。当我们想要对对象进行切片时,希望得到的是一个被切片的对象,而不是一个被切片的数组。 - milad
我因为这个答案而爱你 @Trad - Agent K

37
你可以使用Object.keys函数的结果进行reduce操作。

注解:Object.keys()方法返回一个由指定对象的所有可枚举属性的键名所组成的数组。

const myObject = {
 0: 'zero',
 1: 'one',
 2: 'two',
 3: 'three',
 4: 'four'
};

const sliced = Object.keys(myObject).slice(0, 2).reduce((result, key) => {
                    result[key] = myObject[key];

                    return result;
                }, {});

console.log(sliced);


2
最好的!喜欢它。 - Lu Chinke

36

最好的现代解决方案是将Object.fromEntriesObject.entries结合起来使用。

const foo = {
    one: 'ONE',
    two: 'TWO',
    three: 'THREE',
    four: 'FOUR',
}

const sliced = Object.fromEntries(
    Object.entries(foo).slice(1, 3)
)

console.log(sliced)


1
我很好奇,你有没有将其与其他方法进行基准测试?直觉上似乎有点慢,但我可能错了。在我看来,这绝对是最优雅的解决方案。 - santamanno
1
我还没有测试过。我的猜测是我的解决方案可能会慢一些,但被接受的答案没有返回键,并且在我看来不太干净。 - Kevin Gilbert
1
很好的解决方案,非常干净。 - undefined

12
我试图使用Array.prototype对一个对象进行切片,但它返回一个空数组。这是因为该对象没有.length属性。它会尝试访问它,得到undefined,将其转换为数字,得到0,然后最多从对象中切出那么多个属性。为了达到期望的结果,你需要给它分配一个length或手动迭代对象:

var my_object = {0: 'zero', 1: 'one', 2: 'two', 3: 'three', 4: 'four'};

my_object.length = 5;
console.log(Array.prototype.slice.call(my_object, 4));

var sliced = [];
for (var i=0; i<4; i++)
    sliced[i] = my_object[i];
console.log(sliced);


1
所以我猜没有其他办法,只能手动修改对象,添加长度或迭代器... - JC Garcia
不要被愚弄了,继续阅读,因为@trad现在有最好的答案,还有其他一些聪明的答案可以找到。 - Richard Tyler Miles

5
var obj = {0: 'zero', 1: 'one', 2: 'two', 3: 'three', 4: 'four'};
var result = Object.keys(obj).slice(0,2).map(key => ({[key]:obj[key]}));

console.log(result);

[{'0': '零'}, {'1': '一'}]


3
尝试向my_object添加一个“length”属性,然后你的代码就可以工作了。

    var my_object = {
     0: 'zero',
     1: 'one',
     2: 'two',
     3: 'three',
     4: 'four',
     length: 5
    };
    
    var sliced = Array.prototype.slice.call(my_object, 4);
    console.log(sliced);


2

const res = Object.fromEntries(Object.entries(my_object).slice(0,4));


1
我认为它可以帮助您:

var my_object = { 0: 'zero', 1: 'one', 2: 'two', 3: 'three', 4: 'four' };

var sliced = Object.keys(my_object).map(function(key) { return my_object[key] }).slice(4);

console.log(sliced);

2
带有缺点的是,keys()不能保证以任何特定顺序返回对象的键。 - Ted Hopp

0

// Works, but acts weird when things get serious
// Avoids screwing with the properties of my_object
// You don't get Object.create() until ES5.1
function lazySlice(obj, idx) {
  var newObj = Object.create(obj, { length: {value: Object.keys(obj).length} }),
  idx = idx || 0;

  return Array.prototype.slice.call(newObj, idx);
}

// Only gives you own enumerable properties with keys "0" to n
// Preserves element order (based on key number)
// Ignores non-numbered keys
// You don't get Object.keys() until ES5
function enumSlice(obj, idx) {
  var arr = [], 
    keys = Object.keys(obj),
    idx = idx || 0;

  for (var i = 0; i <= keys.length - 1; i++)
    if (keys[i] >= idx && keys[i] % 1 === 0 && keys[i] >= 0 && keys[i].indexOf('e') < 0) 
      arr.push(obj[keys[i]]);

  return arr;
}

var my_object = {
  0: 'zero',
  1: 'one',
  2: 'two',
  3: 'three',
  4: 'four'
}; 
console.log(lazySlice(my_object, 3));      // [ 'three', 'four' ]
console.log(enumSlice(my_object, 3));      // [ 'three', 'four' ]

var mixed_object = {
   "9": 'nine',
   "2": 'two',
   "1": 'one',
   "7": 'seven',
   "7.5": 'seven point five',
   "1e4": 'sneaky',
   "-4": 'negative four',
   "0": 'zero',
   "length": 35
};
console.log(lazySlice(mixed_object));      // [ 'zero', 'one', 'two', , , , , 'seven',  ]
console.log(enumSlice(mixed_object));      // [ 'zero', 'one', 'two', 'seven', 'nine' ]


0

除非它有一个 [Symbol.iterator] 生成器函数和 存在 length 属性,否则你无法这样做。例如;

var my_object = { 0: 'zero', 1: 'one', 2: 'two', 3: 'three', 4: 'four', length:5 },
       sliced;

my_object[Symbol.iterator] = function* (){
                                          var oks = Object.keys(this);
                                          for (var key of oks) yield this[key];
                                         };

sliced = Array.prototype.slice.call(my_object, 2);
console.log(sliced);


迭代器或长度其中之一就足够了,不需要两者都有。 - Bergi
@Bergi 我之前也考虑过只使用迭代器...我刚刚测试了一下,但是它没有起作用...我不确定仅仅使用 length 属性是否足够。仅仅使用迭代器是不够的。也许有些东西我还没想到。 - Redu
slice 不关心迭代器(它只关心长度),而 Array.from(my_object).slice(4) 则会。我以为当你谈论可迭代对象时,有 Array.from 在考虑范围内... - Bergi
@Bergi 试一下使用.slice(2) - Redu
@Bergi,你说得对,只有 length 属性就可以了。我要改正一下。 - Redu
顺便提一下,在你的生成器函数中,你真的应该使用简单的 for in 枚举方式,而不是通过 Object.keys 来绕路。 - Bergi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接