3

I'm calling a JavaScript function that wants an array of things to display. It displays a count, and displays the items one by one. Everything works when I pass it a normal JavaScript array.

But I have too many items to hold in memory at once. What I'd like to do, is pass it an object with the same interface as an array, and have my method(s) be called when the function tries to access the data. And in fact, if I pass the following:

var featureArray = {length: count, 0: func(0)};

then the count is displayed, and the first item is correctly displayed. But I don't want to assign all the entries, or I'll run out of memory. And the function currently crashes when the user tries to display the second item. I want to know when item 1 is accessed, and return func(1) for item 1, and func(2) for item 2, etc. (i.e., delaying the creation of the item until it is requested).

Is this possible in JavaScript?

Andy Jacobs
  • 933
  • 3
  • 10
  • 18
  • So, when the user calls featArray[0] you want it to execute func(0) and give you the value? Why can't the user just use func(0) instead? – Kevin B Jun 24 '14 at 21:23
  • 3
    If you have too many items to be held in memory, how do you think you have enough memory to display all of them? – Bergi Jun 24 '14 at 21:23
  • where are you getting all these items from? – pseudonym117 Jun 24 '14 at 21:23
  • @Bergi - maybe he's filtering through them, and only displaying some that fit a certain criterion. – Bluefire Jun 24 '14 at 21:24
  • Could you share your code that accepts the array? I'd bet you could make changes in there to accept a different kind of object. – pseudosavant Jun 24 '14 at 21:24
  • I don't own the code of the function that is being called, and can't change it (or I would have just done that). I don't need to display them all at once - just one at a time (thus, the saving of memory). – Andy Jacobs Jun 24 '14 at 21:29
  • No, you can't provide your own operator overload for `[]` in javascript. See http://stackoverflow.com/questions/255041/in-javascript-can-i-override-the-brackets-to-access-characters-in-a-string. – jfriend00 Jun 24 '14 at 21:33
  • Also see [How would you overload the \[\] operator in javascript](http://stackoverflow.com/questions/1711357/how-would-you-overload-the-operator-in-javascript). – jfriend00 Jun 24 '14 at 21:40
  • 1
    Well, it would help seeing pieces of your code. From where those items come from? How are they getting generated? – plalx Jun 24 '14 at 21:42

4 Answers4

0

Yes, generating items on the go is possible. You will want to have a look at Lazy.js, a library for producing lazily computed/loaded sequences.

However, you will need to change your function that accepts this sequence, it will need to be consumed differently than a plain array.


If you really need to fake an array interface, you'd use Proxies. Unfortunately, it is only a harmony draft and currently only supported in Firefox' Javascript 1.8.5.

Assuming that the array is only accessed in an iteration, i.e. starting with index 0, you might be able to do some crazy things with getters:

var featureArray = (function(func) {
    var arr = {length: 0};
    function makeGetter(i) {
        arr.length = i+1;
        Object.defineProperty(arr, i, {
            get: function() {
                var val = func(i);
                Object.defineProperty(arr, i, {value:val});
                makeGetter(i+1);
                return val;
            },
            configurable: true,
            enumerable: true
        });
    }
    makeGetter(0);
    return arr;
}(func));

However, I'd recommend to avoid that and rather switch the library that is expecting the array. This solution is very errorprone if anything else is done with the "array" but accessing its indices in order.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • The constraint appears to be that the data must be fetched via array indexing. – jfriend00 Jun 24 '14 at 21:52
  • 1
    @Bergi Hum, very clever. Haven't thought about generating getters on demand. However the OP's question doesn't seem to make much sense. Unless his items comes from a generator function (could be infinite) I do not get how he plans to return them independently of the approach used if there isin't enough memory. If they come from a web service, he could probably just split the processing in multiple batches. – plalx Jun 24 '14 at 22:02
  • Yeah, I neither understand the exact arrangement of the OPs code and his problem. It looks like he has a processing function he can't change so that it works with chunks of data. – Bergi Jun 24 '14 at 22:08
0

If I understand correctly, this would help:

var object = {length: count, data: function (whatever) {
    // create your item
}};

Then, instead of doing array[1], array[2], et cetera, you'd do object.data(1), object.data(2), and so on.

Bluefire
  • 13,519
  • 24
  • 74
  • 118
  • If I could change the code, that would work. But the function being called is not mine, and is expecting an array. – Andy Jacobs Jun 24 '14 at 21:31
0

Since there seems to be a constraint that the data must be accessed using array indexing via normal array indexing arr[index] and that can't be changed, then the answer is that NO, you can't override array indexing in Javascript to change how it works and make some sort of virtual array that only fetches data upon demand. It was proposed for ECMAScript 4 and rejected as a feature.

See these two other posts for other discussion/confirmation:

How would you overload the [] operator in Javascript

In javascript, can I override the brackets to access characters in a string?

The usual way to solve this problem would be to switch to using a method such as .get(n) to request the data and then the implementor of .get() can virtualize however much they want.


P.S. Others indicate that you could use a Proxy object for this in Firefox (not supported in other browsers as far as I know), but I'm not personally familiar with Proxy objects as it's use seems rather limited to code that only targets Firefox right now.

Community
  • 1
  • 1
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • 1
    You could do it using [Proxy](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Proxy), as long as it's supported. – plalx Jun 24 '14 at 21:47
  • @plalx - feel free to explain. – jfriend00 Jun 24 '14 at 21:47
  • @plalx - can you show or reference an example of using a proxy (which only appears to be supported in Firefox right now) to overload array indexing? – jfriend00 Jun 24 '14 at 21:50
  • Sure, something like `var arrProxy = new Proxy({}, { get: function (target, name) { if (/*name is a number*/) return /*some computation*/; } });` Basically a proxy let's you intercept every property access on a target object. You would also have to implement the rest of the array interface obviously. – plalx Jun 24 '14 at 21:55
0

Thank you to everyone who has commented and answered my original question - it seems that this is not (currently) supported by JavaScript.

I was able to get around this limitation, and still do what I wanted. It uses an aspect of the program that I did not mention in my original question (I was trying to simplify the question), so it is understandable that other's couldn't recommend this. That is, it doesn't technically answer my original question, but I'm sharing it in case others find it useful.

It turns out that one member of the object in each array element is a callback function. That is (using the terminology from my original question), func(n) is returning an object, which contains a function in one member, which is called by the method being passed the data. Since this callback function knows the index it is associated with (at least, when being created by func(n)), it can add the next item in the array (or at least ensure that it is already there) when it is called. A more complicated solution might go a few ahead, and/or behind, and/or could cleanup items not near the current index to free memory. This all assumes that the items will be accessed consecutively (which is the case in my program).

E.g.,

1) Create a variable that will stay in scope (e.g., a global variable).

2) Call the function with an object like I gave as an example in my original question:

var featureArray = {length: count, 0: func(0)};

3) func() can be something like:

function func(r) {
    return {
        f : function() {featureArray[r + 1] = func(r + 1); DoOtherStuff(r); }
    }
}

Assuming that f() is the member with the function that will be called by the external function.

Andy Jacobs
  • 933
  • 3
  • 10
  • 18