0

I have this object:

var registered_screens = {
    handle_1 : 'step_1',
    handle_2 : 'step_2',
    handle_3 : 'step_3'
};

And here's what I tried to get its neighbors:

function get_neighbors( current_step ) {
    var steps = {
        next: '',
        previous: ''
    };

    for( var i in registered_screens ) {
        if( current_step == registered_screens[i] ) {
            if(registered_screens.hasOwnProperty(registered_screens[i])) {
                steps.next = registered_screens[i+1];
            }

            if(registered_screens.hasOwnProperty(registered_screens[i-1])) {
                steps.previous = registered_screens[i-1];
            }
        }
    }

    return steps;
}

Obviously, this is a no-go because an object can't be parsed the same as an array, but just wanted to show what I tried.

What I'd want to get is, if I call get_neighbors('handle_2'), return:

steps { prev : 'handle_1' , next : 'handle_3' }

Or, for get_neighbors('handle_3'):

steps { prev : 'handle_2', next : null }

I've also attempted:

var registered_screens = {
    handle_one : 'step_1',
    handle_2 : 'step_2',
    handle_3 : 'step_3'
};

var key_values = Object.keys(registered_screens);

for( var i = 0; i < key_values.length; i++ ) {
    console.log(registered_screens.key_values[i]);
}

But this throws:

main.js?ver=4.9.6:18 Uncaught TypeError: Cannot read property '0' of undefined
main.js?ver=4.9.6:18
main.js?ver=4.9.6:270

Funnily enough, I checked what the values for key_values[i] are and they're the right handles.

It seems JS has a hard time building variables out of strings?

coolpasta
  • 675
  • 4
  • 15
  • 1
    Why are you using an object instead of an array? – Tigger Jun 10 '18 at 04:11
  • @Tigger It's necessary to pull out the data by a handle, such as `registered_screens.handle1`. – coolpasta Jun 10 '18 at 04:14
  • do you have any chance to get this data as a Map or an Array of objects? JS does not guarantee any kind of object key ordering – wiesion Jun 10 '18 at 04:22
  • @wiesion I believe that can be done, thanks for the tip, but this object is non-dynamic, as such, isn't the ordering a non-issue? Edit: I just printed the object for 10,000 times, it maintains the order, at least at access. – coolpasta Jun 10 '18 at 04:25
  • 1
    Just FYI: In `JavaScript`, [`Object` `property` order is not guaranteed](https://stackoverflow.com/questions/5525795/does-javascript-guarantee-object-property-order/5525820#5525820). You might want to explore using a [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map). – Arman Charan Jun 10 '18 at 04:27
  • @Tigger `steps : { prev : '', next : 'handle_2' };` and `steps : { prev : 'handle_2', next : '' };` – coolpasta Jun 10 '18 at 04:31
  • 2
    object key ordering is browser dependent and quite shady, for instance see https://bugs.chromium.org/p/v8/issues/detail?id=164 - just because it works in a version of a browser, don't rely on it – wiesion Jun 10 '18 at 04:32

5 Answers5

1

Your second attempt is what I thought as well, it should work fine if you correct the key access like below

var registered_screens = {
    handle_one : 'step_1',
    handle_2 : 'step_2',
    handle_3 : 'step_3'
};

var key_values = Object.keys(registered_screens);

for( var i = 0; i < key_values.length; i++ ) {
    console.log(registered_screens[key_values[i]]);
}

As far as I know, the keys in the object are un-ordered and we should not rely on that order, like others have mentioned, it's order will not be the same as when you created, but your requirement seems like it can make use of Object.keys to iterate and find next and prev keys to some extent

To your question, why this registered_screens[key_values[i]] works and not registered_screens.key_values[i], the dot notation will not work for dynamic keys, i.e key_values[i] is not a key, it's a variable holding the key, in such cases you have to access it like an array like Object[keyNameHolder]

Ajanth
  • 1,820
  • 1
  • 16
  • 22
1

I don't like it much and would look at a restructure to make registered_screens an array. I also would not trust this code due to object order can not be guaranteed.

That said, this will work with my browser.

Edit: Added an array version, which I would trust but would expand for blank (undefined) results.

// array version
var aScreens = [];
    aScreens['handle_one'] = 'step_1';
    aScreens['handle_2'] = 'step_2';
    aScreens['handle_3'] = 'step_3';

function getArrayPrevNext(a,current) {
    var x,p = '',n = '',found = false;
    for(x in a) {
        if (found) {
            n = x;
            break;
        } else if (x == current) {
            found = true;
        } else {
            p = x;
        }
    }
    return {prev:p,next:n};
}

var aSteps = getArrayPrevNext(aScreens,'handle_3');
console.log('array prev['+ aSteps.prev +'], next['+ aSteps.next +']');
var p = aSteps.prev, n = aSteps.next;
console.log('handle prev['+ aScreens[p] +'], next['+ aScreens[n] +']');
console.log('handle alt prev['+ aScreens[aSteps.prev] +'], next['+ aScreens[aSteps.next] +']');


// Object version
var registered_screens = {
    handle_one : 'step_1',
    handle_2 : 'step_2',
    handle_3 : 'step_3'
};


function getPreviousNext(obj,current) {
    var prev = '', nxt = '', found = false;
    Object.keys(obj).forEach(function(key) {
       if (! nxt) {
           if (found) {
               nxt = key; 
           } else if (key == current) {
               found = true;
           } else {
               prev = key; 
           }
       }
    });
    return {prev:prev,next:nxt};
}

var steps = getPreviousNext(registered_screens,'handle_3');
console.log('Object steps:['+ steps.prev +']['+ steps.next +']');
Tigger
  • 8,307
  • 4
  • 32
  • 38
  • I've just loaded up my testing suite and it all works, logging 10,000 times for each attempt, with stuff in-between. Is order still not guaranteed even if my object is always static and can never change? – coolpasta Jun 10 '18 at 04:40
  • Have a read over: [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) and [Does JavaScript Guarantee Object Property Order?](https://stackoverflow.com/questions/5525795/does-javascript-guarantee-object-property-order/32149345#32149345) – Tigger Jun 10 '18 at 04:43
  • Damn. What's my alternative to being able to pull data out by a handle? The array obviously has no `k => v` properties. – coolpasta Jun 10 '18 at 04:47
  • I've worked my own too: https://pastebin.com/A12ySPaW but it throws the `Uncaught TypeError: Cannot read property '0' of undefined` error, even if I'm checking for it. Any ideas? – coolpasta Jun 10 '18 at 05:24
1

This script does a very basic parsing of object keys - it assumes it is always in the format of handle_{n} - based on that it creates an array that holds the keys in proper order, which then is searched for and uses n-1 and n+1 to return prev and next (if possible, else null). And yes i know, most browsers would sort it correctly nonetheless so that in most scenarios you would get the proper order (included a console output for comparison)

var screens = {
      handle_1 : 'step_1',
      handle_2 : 'step_2',
      handle_3 : 'step_3',
      handle_4 : 'step_4',
      handle_5 : 'step_5',
      handle_6 : 'step_6',
      handle_7 : 'step_7',
      handle_8 : 'step_8',
      handle_9 : 'step_9',
      handle_10 : 'step_10',
      handle_11 : 'step_11',
    },
    keyParser = (key) => parseInt(key.replace('handle_', '')),
    keySorter = (a, b) => keyParser(a) - keyParser(b),
    handleKeys = Object.keys(screens).sort(keySorter);

// Compare key ordering in your browser
// It will be most likely identic, since most modern browsers understand that
// it should sort by {str}_{int} and not by {str}_{str} if an {int} is present
// but chances are that for instance IE9 would do the latter, so it could be that
// with browser ordering handle_10 and handle_11 come after handle_1
console.log('browser ordering:', Object.keys(screens));
console.log('parsed ordering:', handleKeys);

function getSteps(handle) {
  var pos = handleKeys.indexOf(handle);
  if(pos === -1) throw(`Can't find handle ${handle} in screens`);
  return {
    current: screens[handleKeys[pos]],
    prev: pos > 0 ? screens[handleKeys[pos-1]] : null,
    next: pos < handleKeys.length-1 ? screens[handleKeys[pos+1]] : null
  }
}

console.log(
  getSteps('handle_1'),
  getSteps('handle_2'),
  getSteps('handle_6'),
  getSteps('handle_10'),
  getSteps('handle_11')
);

Also a good read: https://hackernoon.com/out-of-order-keys-in-es6-objects-d5cede7dc92e

wiesion
  • 2,118
  • 8
  • 19
0

As far as I know the answer is there is no direct way, but you can play around this in many ways.

One idea is that you can play with the naming convention of your data, for example, call the object items as handle_1 instead of "handle_one" and so on, that way you can loop around the array using index ['handle_' + i] but notice that you can't do ['handle_' + i + 1] or else you will have a wrong index value because the string conversion will happen before the summation.

I hope this helps.

Labib Ismaiel
  • 1,220
  • 2
  • 10
  • 21
0

I made this work:

var registered_screens = {
    handle_1 : 'step_1',
    handle_2 : 'step_2',
    handle_3 : 'step_3'
};


function get_neighbors( current_step ) {
    var steps = {
        next: '',
        previous: ''
    };

    var key_values = Object.keys(registered_screens);

    for( var i = 0; i < key_values.length; i++ ) {
        if( current_step == registered_screens[key_values[i]]) {
            if( !(registered_screens[key_values[i-1]] == null) ) {
                steps.previous = registered_screens[key_values[i-1]];
            }
            if( !(registered_screens[key_values[i+1]] == null) ) {
                steps.next = registered_screens[key_values[i+1]];
            }
        }
    }

    return steps;
}

And so, get_neighbors('step_2') reliably (in my tests) returns:

steps : { next : 'step_3', previous: 'step_1' };

coolpasta
  • 675
  • 4
  • 15