4

I have an object with keys and data. From a key I want to get the previous item.

I've seen similar topics before but the solutions are really complicated.

I also added a function that does not work but how I wish it would work

Simplified code

let obj = {
  slug:        'my-page',
  title:       'My title',
  description: 'My description',
  content:     'My content'
};

let previous = obj.previousItem('description'); // Does not work

console.log(previous);

Expected result

{ title: 'My title' }

  • ES5 or even ES6 is fine
  • I prefer a simple solution to a complicated one, maybe without loops
Jens Törnell
  • 18,167
  • 36
  • 100
  • 168
  • 3
    You should use an array if you rely on the order. – str Jun 11 '19 at 11:28
  • 4
    Possible duplicate of [Does ES6 introduce a well-defined order of enumeration for object properties?](https://stackoverflow.com/questions/30076219/does-es6-introduce-a-well-defined-order-of-enumeration-for-object-properties) – str Jun 11 '19 at 11:29
  • @str Why is that? Will the order be lost? – Jens Törnell Jun 11 '19 at 11:29
  • It might be. See the duplicate question for more information. – str Jun 11 '19 at 11:30
  • 1
    Generally speaking, object properties are *unordered*. – deceze Jun 11 '19 at 11:30
  • 2
    Although most browsers have a fixed iteration order these days, the order of properties is NOT guaranteed by the specification for all JS versions before the newest ones. So if you have to be 100% sure about an order of something, use an array. And in this case, it';s rather simple. A basic 'pagination' wrapper where you save the current index and have `.prev()` and `.next()` subtract/add to the index and return. A more complex version can be made with iterators. – Shilly Jun 11 '19 at 11:33
  • 1
    An alternative to the array is a Map, which will preserve the insert order. But overall, there is plenty of [dupes](https://stackoverflow.com/questions/5525795/does-javascript-guarantee-object-property-order) for this. – VLAZ Jun 11 '19 at 11:34
  • After some research I think map is the way to go `let map = new Map(Object.entries(obj));` just like VLAZ said. – Jens Törnell Jun 12 '19 at 06:40
  • @JensTörnell Not really. The property order of `Object.entries` is *not* specified. Using it to create a `Map` won't change that. Either use a `Map` exclusively (i.e. no plain object) or use something like `Object.getOwnPropertyNames` to create the `Map`. – str Jun 12 '19 at 13:00

4 Answers4

-2

Object used to be to have no order, but actually they have and I would not rely on this order.

But anyway, you could filter by the next key/value pair and get an object.

let obj = { slug: 'my-page', title: 'My title', description: 'My description', content: 'My content' },
    previous = Object.fromEntries(Object
        .entries(obj)
        .filter((_, i, { [i + 1]: next = [] }) => next[0] === 'description')
    );

console.log(previous);
Nina Scholz
  • 323,592
  • 20
  • 270
  • 324
  • 2
    While this will work as intended, object property order is not guaranteed and thus, this is a bad idea in general. Quote from object definition: `4.3.3 Object An object is a member of the type Object. It is an unordered collection of properties each of which contains a primitive value, object, or function.` However, ES6 `Map` is similar and *will* guarantee the order. So i recommend using that instead – buffy Jun 11 '19 at 11:44
  • i know this, assuming op know this, too. – Nina Scholz Jun 11 '19 at 11:46
-3

I think somethink like this

Object.prototype.previousItem = function(propName) {
  const arr = Object.keys(this);
  let indx = arr.indexOf(propName)-1;
  if (indx<0) return null;
  const result = {};
  result[arr[indx]] = this[arr[indx]];
  return result;
}
Yaroslav Gaponov
  • 1,598
  • 9
  • 9
  • 2
    It would be better to use `Object.getOwnPropertyNames`. ES6 requires this to maintain order, `Object.keys` isn't required. – Barmar Jun 11 '19 at 11:37
  • Not sure. The ordering of the enumerable properties in the array is consistent with the ordering exposed by a for...in loop (or by Object.keys()) over the properties of the object. – Yaroslav Gaponov Jun 11 '19 at 11:43
  • [Why is extending native objects a bad practice?](https://stackoverflow.com/questions/14034180/why-is-extending-native-objects-a-bad-practice) – str Jun 11 '19 at 11:43
  • yes, but i want just run Jens code, not create the best app. In any case this is very strange desire – Yaroslav Gaponov Jun 11 '19 at 11:44
  • See https://stackoverflow.com/questions/30076219/does-es6-introduce-a-well-defined-order-of-enumeration-for-object-properties. It says that ES6 doesn't require ordering for `for-in` or `Object.keys()`, but it does require ordering for a number of other Object methods. – Barmar Jun 11 '19 at 12:16
  • So they're consistent with each other, but not required to be the same as the order that the properties were created. – Barmar Jun 11 '19 at 12:17
  • I think if we inject previousItem to object, after we can on first call keep keys in array and after re-use it for next calls. So in this case we will have only 1 call for Object.keys and order is not impotent . – Yaroslav Gaponov Jun 11 '19 at 12:22
-3

Object.prototype.previousItem = function(propName) {
  const arr = Object.keys(this);
  let indx = arr.indexOf(propName)-1;
  if (indx<0) return null;
  const result = Object.create(null); // just clear output
  result[arr[indx]] = this[arr[indx]];
  return result;
}

let obj = {
  slug:        'my-page',
  title:       'My title',
  description: 'My description',
  content:     'My content'
};

let previous = obj.previousItem('description'); //  work

console.log(previous);
Yaroslav Gaponov
  • 1,598
  • 9
  • 9
-3

You can try following to get the previous item

let object = {
    slug: 'my-page',
    title: 'My title',
    description: 'My description',
    content: 'My content'
}
let previous = previousItem(object, 'description');
console.log(previous);


function previousItem(object, str) {
    let lastKey = "";

    for (const key in object) {
        if (object.hasOwnProperty(key)) {
            if (key == str){
                lastKey = lastKey || key;
                let obj = { };
                obj[lastKey] = object[lastKey];
                return obj;
            } else {
                lastKey = key;
            }
        }
    }
}