Jump to content

Creating lists of blocks/entities


mattstyles
 Share

Recommended Posts

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'.

Link to comment
Share on other sites

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 ?)

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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

 

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...