46

I was trying to slice an object using Array.prototype, but it returns an empty array, is there any method to slice objects besides passing arguments or is just my code that has something wrong? Thx!!

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);
Kevin Katzke
  • 3,581
  • 3
  • 38
  • 47
JC Garcia
  • 615
  • 2
  • 7
  • 11
  • Closest you can get to that is deleting the key with `delete object.property` and making sure you've stored that property in a separate variable before then. – Jeremy Jackson Sep 05 '16 at 19:26
  • What is your expected output? Slicing an object makes no sense - it doesn't mean anything, but if you can explain what you'd like your expected output to be we can work to get there. – winhowes Sep 05 '16 at 19:37
  • I try to create a new array, only with the properties that I could need, i.e: var obj = {car: 'audi', engine: 'diesel', color: 'blue'} as output I would like a new object only with the "color" property. – JC Garcia Sep 05 '16 at 19:42
  • *I would like a new object* - but `Array.slice` returns an array – RomanPerekhrest Sep 05 '16 at 19:46

11 Answers11

69

Nobody mentioned Object.entries() yet, which might be the most flexible way to do it. This method uses the same ordering as for..in when enumerating properties, i.e. the order that properties were originally entered in the object. You also get subarrays with both property and value so you can use whichever or both. Finally you don't have to worry about the properties being numerical or setting an extra length property (as you do when using Array.prototype.slice.call()).
Here's an example:

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

You want to slice the first two values:

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

All of the keys?

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

The last key-value pair?

Object.entries(obj).slice(-1)
//[ ['prop4', {'prop': 'buzz'}] ]
trad
  • 7,581
  • 4
  • 20
  • 17
  • 9
    `Object.entries` is certainly a useful method to know about (new with ECMA 262, apparently) ... but it seems just to produce an array of arrays, which isn't really what the OP was asking for. – mike rodent Nov 16 '17 at 10:13
  • 1
    Object.entries is not supported in IE. – MBouwman Jul 05 '19 at 08:58
  • 3
    Unfortunately This solution will result in a new Array and not a new Object. When we want to slice an Object, we expect a sliced Object and not a sliced Array. – milad Sep 22 '20 at 18:10
  • I love you for this answer @Trad – Agent K Jun 16 '21 at 17:35
37

You can reduce the result of Object.keys function

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);
Amerzilla
  • 1,212
  • 12
  • 12
33

The best modern solution to this is the combination of Object.fromEntries and Object.entries.

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

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

console.log(sliced)
Kevin Gilbert
  • 903
  • 8
  • 21
  • 1
    I'm curious, did you benchmark this against other methods? Istinctively seems slow but I might be wrong. It's definitely the most elegant solution imho. – santamanno Sep 01 '21 at 17:58
  • 1
    I haven't tested it. My guess is my solution is a little bit slower but the accepted answer is not returning the keys and is not very clean in my opinion. – Kevin Gilbert Sep 26 '21 at 00:31
12

I was trying to slice an object using Array.prototype, but it returns an empty array

That's because it doesn't have a .length property. It will try to access it, get undefined, cast it to a number, get 0, and slice at most that many properties out of the object. To achieve the desired result, you therefore have to assign it a length, or iterator through the object manually:

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);
arhak
  • 2,488
  • 1
  • 24
  • 38
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
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': 'zero' }, { '1': 'one' } ]

rekinyz
  • 6,576
  • 2
  • 29
  • 32
3

Try adding a 'length' property to my_object and then your code should work:

    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);
giorgio79
  • 3,787
  • 9
  • 53
  • 85
Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
2

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

I think it can helps you:

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
    with the downside that `keys()` is not guaranteed to return the keys for the object in any particular order. – Ted Hopp Sep 05 '16 at 19:43
0

You can't unless it has a [Symbol.iterator] generator function and length property exists. Such as;

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);
Redu
  • 25,060
  • 6
  • 56
  • 76
  • An iterator *or* a length is enough, you don't need both. – Bergi Sep 05 '16 at 19:46
  • @Bergi That's what i had thought about having just an iterator... i've just tested with an iterator an it didn't work.. I am not sure with only `length` property it would be OK though. No just an iterator won't cut. May be there is something i am missing. – Redu Sep 05 '16 at 19:50
  • `slice` doesn't care for an iterator (it only cares for the length), `Array.from(my_object).slice(4)` would. I thought you had `Array.from` in mind when talking about iterables… – Bergi Sep 05 '16 at 19:52
  • @Bergi try it with `.slice(2)` – Redu Sep 05 '16 at 19:53
  • @Bergi OK you are right with only `length` property it's fine. Gotto correct. – Redu Sep 05 '16 at 19:55
  • Btw, in your generator function you really should use a simple `for in` enumeration instead of making the detour via `Object.keys` – Bergi Sep 05 '16 at 19:55
0

You don't mention it in your question, but that looks awfully a lot like an arguments object.

Convert it to an array using Array.from() then use it like any other array. As long as it is an enumerable object.

For a polyfill for older browsers, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from

jishi
  • 24,126
  • 6
  • 49
  • 75
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' ]
Karim Temple
  • 171
  • 1
  • 2