mattstyles Posted February 2, 2017 Share Posted February 2, 2017 Anyone know of a nice way to create lists of objects (such as terrain blocks or entity lists) that can inherit from each other? I've tried to reduce my problem, which basically consists of wanting a nice clean pattern to do something like the following: var list = [ { id: 'base', prop: 'base_something' }, { id: 'somethingelse', prop: 'anotherprop' }, { ...list[0], id: 'block' } ] What I want is that the object with id of 'block' ends up looking like: { prop: 'base_something', id: 'block } But, obviously, the code above won't work because I can't access the 'list' array until it has been fully instantiated. Anyone know a nice pattern for doing something like this? I don't really need the data as json, it can have the advantages of being JS. The 'best' idea I have so far is a post-process so each object contains an array of strings representing which objects it extends/inherits from and then we loop over the instantiated list and massage the base props on to the child objects. Just sounds bit 'bleurgh'. Quote Link to comment Share on other sites More sharing options...
Théo Sabattié Posted February 2, 2017 Share Posted February 2, 2017 I am not sure to understand... Do you want the list reference in each object in it? like var list = [ { id: 'base', prop: 'base_something', myList: // == list }, { id: 'somethingelse', prop: 'anotherprop', myList: // == list }, { id: 'somethingelse42', prop: 'anotherprop42', myList: // == list }, ] ? If it is that, you can do: var list = []; function ListElement(parameters){ // replace name making sens this.list = parameters.list || []; this.list.push(this); this.prop = parameters.prop || "defaultProp"; this.id = parameters.id || "defaultId"; // your own properties instantiation } new ListElement({ list : list, prop : "hello", id : "world" }); Or do you want that the last object of your list have the same property of the first element? Do you want that the last object has alway the same property of first? (if first remove, take new property of new first. If last remove, set property of first on new last ?) Quote Link to comment Share on other sites More sharing options...
mattstyles Posted February 2, 2017 Author Share Posted February 2, 2017 Thanks for the reply Theo No, I don't need a ref to the list, just to members of that array. Part of the complexity is that I don't know how many sources are going to construct this 'master' list, so I briefly consider structuring the lists, i.e. generate a base list, then an 'inherited' list which references items from the base array, but, that introduces the concept of order (a common complaint with inheritance) so I don't want to do that. Due to this I'm thinking that running a post-process over the array would be best, well, maybe not best but it would work (a little like your 2nd example). So I create a master list from numerous sources, each of those items can specify what they want to inherit from i.e. // Initial declaration var master = [ { id: 'base:one', prop: 'foo' }, { id: 'core:one', extend: 'base:one' } ] // Part of initial declaration e.g from other sources master = master.concat([ { id: 'module:one', prop: 'bar' } ]) // Post process master = master.map(item => { if (!item.extend) { return item } let base = master.find(n => n.id === item.extend) return { ...base, ...item } }) Extending this a little bit (to nuke the extend key and use a list of bases to extend from) should work ok, it just feels a little clunky but maybe it gives a bit more control. Quote Link to comment Share on other sites More sharing options...
b10b Posted February 2, 2017 Share Posted February 2, 2017 I am trying to follow. Is there a risk with a two-pass that some scenarios might be circular and un-resolvable? Have you considered a non static list - e.g. a getter for "prop", returning a Composite Pattern interface? Apologies if I am off the mark. Quote Link to comment Share on other sites More sharing options...
mattstyles Posted February 3, 2017 Author Share Posted February 3, 2017 7 hours ago, b10b said: Is there a risk with a two-pass that some scenarios might be circular and un-resolvable? I hadn't actually considered this, and that would be a potential problem. I could handle that in the 'parent' resolution algorithm though, using a recursive function to get references should work, simplest is probably to limit the recursion depth although that might create an inaccuracy if a really long chain is setup. Hmm, getting more complex now 7 hours ago, b10b said: Have you considered a non static list - e.g. a getter for "prop", returning a Composite Pattern interface? Thats a good idea, but I'd worry about performance. I have a map structure that references these objects and I'm trying to avoid actually 'instantiating' each one i.e. I don't want individual instances of these structures to make up my map, I just want refs. I should probably test this for perf though. Thanks. Quote Link to comment Share on other sites More sharing options...
BobF Posted February 3, 2017 Share Posted February 3, 2017 Could you perform the inheritance as the items are added, rather than post-processing? Something like this: var masterList = []; masterList.add = function (item) { if (Array.isArray(item)) { item.forEach(function (item) { masterList.add(item); }); } else { if (item.extend) { // Find the base item in "masterList" and mix it into "item" } this.push(item); } }; masterList.add([ { id: 'base:one', prop: 'foo' }, { id: 'core:one', extend: 'base:one' } ]); // Continue to add other items to the list as needed Quote Link to comment Share on other sites More sharing options...
mattstyles Posted February 4, 2017 Author Share Posted February 4, 2017 Yep, that'd work, but it would be dependent on parents being added to the list before children, the post-process removes this restriction (although circular refs still need to be handled, which could get tricky with complex inheritance, but, meh, inheritance sucks ha ha). Quote Link to comment Share on other sites More sharing options...
BobF Posted February 4, 2017 Share Posted February 4, 2017 8 hours ago, mattstyles said: but it would be dependent on parents being added to the list before children Yeah, I assumed you could ensure the base items are added first. If that's not possible then my solution could be modified as: var masterList = []; masterList.add = (function () { var pendingItems = []; var resolvePendingItems = function (newItem) { // Resolve any pending items that are dependent on the new item }; return function (item) { if (Array.isArray(item)) { item.forEach(function (item) { masterList.add(item); }); } else { if (item.extend) { if (base_item_is_present) { // Find the base item in "masterList" and mix it into "item" } else { pendingItems.push(item); return; } } this.push(item); resolvePendingItems(item); } }; })(); masterList.add([ { id: 'base:one', prop: 'foo' }, { id: 'core:one', extend: 'base:one' } ]); // Continue to add other items to the list as needed You still have the messiness of the post-processor (e.g., dealing with multiple unresolved levels of inheritance, circular checks, etc) but it is now moved to "resolvePendingItems" and encapsulated inside "masterList" where it won't be seen externally. Obviously I don't know your problem domain, but it seems your item list logic will be much simpler and more robust if there's any reasonable way you can ensure base items are added before their child items. mattstyles 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.