Deleting list elements

mrangel
14 Sept 2017, 11:29

And here I am with a really basic question. I'm probably just missing something in the docs.

I want to append something to the last string in a list. In most languages, it would look something like order_queue[n] += " and fries". But I can't work out how to do it in Quest.

My first thought would be to get the item, add a "=" to the end of it, remove the old value, and add the new one. But from the docs, list remove takes a value, not an index. So how do I know it's removing the right one if the list is expected to contain duplicates values? And that wouldn't be useful for situations where you want to change a list item other than the last.

I could loop over the list, copying all the values into a new list, and changing the one I want to change. But seriously, is there not even a core SetListItem(list, index, value)function? Or is it just missing from the documentation, or in a really non-obvious place?


hegemonkhan
14 Sept 2017, 14:40

here's the docs:

http://docs.textadventures.co.uk/quest/using_lists.html
http://docs.textadventures.co.uk/quest/using_dictionaries.html

list_count_integer_variable = ListCount (LIST)
last_index_number_integer_variable = list_count_integer_variable - 1
last_item_string_variable = StringListItem (LIST, last_index_number_integer_variable)
modified_last_item_string_variable = last_item_string_variable + " and fries"
list remove (last_item_string_variable)
list add (modified_last_item_string_variable)

// if you like better, you can combine these into single lines (sometimes I like to do it and sometimes I don't, and/or it depends on the design too, though if you do combine into single lines... make sure you got the correct number of and location of parenthesis, lol, easy to mess up when combining as single lines):

last_item_string_variable = StringListItem (LIST, ListCount (LIST) - 1)
modified_last_item_string_variable = last_item_string_variable + " and fries"
list remove (last_item_string_variable)
list add (modified_last_item_string_variable)

mrangel
14 Sept 2017, 14:43

HK: I already said why that doesn't work.


hegemonkhan
14 Sept 2017, 14:48

oh my bad... unfortunately I don't think there is at the user-level, but there might be an underlying/core Function that does it... you'd think there'd have to be some kind of Function to create the list/dictionary/array... in the underlying/core code...


HK edit:

you could maybe do this:

'join' to turn list into a string, then 'Instr/RevInstr' ... maybe...

http://docs.textadventures.co.uk/quest/functions/string/instr.html

http://docs.textadventures.co.uk/quest/functions/#string


you might have to unfortunately make a pointer-node like if you were making a linked list... to get the item you want from the list...


you could jusr use a Dictionary Attribute instead ... might be easier than trying to figure out how with a list


ya, unfortunately quest doesn't seem to have these Functions at the user level... you'd have to use 'for' Function to create your own Function that creates indexes for you to then use... basically you got to create a pointer-node like if you were creating a linked list data structure, and use those 'pointer-node' Functions for your List Attribute...


The Pixie
14 Sept 2017, 15:06

Lists are pretty limited. You also can insert an element part way through a list. You are pretty much restricted to either considering the immutable, so positioning is preserved, or as a bag, where you only change it by adding to it or removing from it without worrying about position (that said, I have been messing with Quest code for six years and have not found the need to do anything more than that).


mrangel
14 Sept 2017, 15:15

In the case I'm looking at, it's probably easier to have a separate variable to store the number of the last element, and use a dictionary whose keys are numbers.

But just in case anyone else has a need for this; off the top of my head:

<function name="SetListItem" parameters="list, index, value" type="list">
  newlist = NewList()
  final = ListCount(list) - 1
  if (index > final) {
    final = index
  }
  for (i, 0, final) {
    if (i = index) {
      list add (newlist, value)
    }
    else if (i >= ListCount(list)) {
      // if we want to add a new element after the end of a list, pad it out
      list add (newlist, null)
    }
    else {
      item = GetListItem(list, i)
      list add (newlist, item)
    }
  }
  return (newlist)
</function>

(really inefficient. And I really hope there's a hard-coded function to do that, but if there is I haven't been able to find it)


mrangel
14 Sept 2017, 15:25

(just read the edits)
HK: Yeah, a linked list might actually make more sense.

I did try the Join/Split method previously. But every time I've tried to use Join, it gives an error relating to variable names that aren't in my code; and I have no idea why.


K.V.
14 Sept 2017, 18:08

Are you talking about something like this?

room.string = (Split("one;two;three", ";"))
msg (room.string)
n = ListItem(room.string, ListCount(room.string) - 1)
msg (n)
o = n + "_addon"
room.string = room.string - n + o
msg (room.string)

Doctor Agon
14 Sept 2017, 19:50

Are you wanting to add/remove something to/from the end of your list, or the middle of your list.


K.V.
14 Sept 2017, 20:53

Using something closer to your example:

game.order = (Split("burger;chicken nuggets;fish sandwich", ";"))
msg (game.order)
n = ListItem(game.order, ListCount(game.order) - 1)
msg (n)
o = n + " and fries"
game.order  = game.order - n + o
msg (game.order)

OUTPUT:

List: burger; chicken nuggets; fish sandwich;
fish sandwich
List: burger; chicken nuggets; fish sandwich and fries;


K.V.
14 Sept 2017, 20:59

I had that written in one line, but strange things happened.

For instance:

game.order = (Split("burger;chicken nuggets;fish sandwich", ";"))
msg (game.order)
n = ListItem(game.order, ListCount(game.order) - 1)
msg (n)
//o = n + " and fries"
//game.order  = game.order - n + o
game.order  = game.order - n + n + " and fries"
msg (game.order)

Yields:

You are in a room.
List: burger; chicken nuggets; fish sandwich;
fish sandwich
List: burger; chicken nuggets; fish sandwich; and fries;


UPDATE:

Ha-ha!

Fixed that one:

game.order = game.order - n + (n + " and fries") works correctly.

List: burger; chicken nuggets; fish sandwich and fries;


This is a simplified as I can get it:

game.order = (Split("burger;chicken nuggets;fish sandwich", ";"))
n = ListItem(game.order, ListCount(game.order) - 1)
game.order  = game.order - n + (n + " and fries")
msg (game.order)

If I try to bypass creating the n variable (and I tried 4 or 5 different ways), it throws an arithmetic error.

>Error running script: Error compiling expression 'game.order - ListItem(game.order, ListCount(game.order) - 1) + (n + " and fries")': ArithmeticElement: Operation 'Subtract' is not defined for types 'QuestList`1' and 'Object'


The Pixie
14 Sept 2017, 21:06

This should work:

game.order  = game.order - n + (n + " and fries")

K.V.
14 Sept 2017, 21:33

Whoa!

I came up with the same answer as Pixie that time! (Whoo-hoo!)

(Of course, Pixie probably didn't 'come up with it'. Probably just knew... But still: Whoo-hoo!)


mrangel
14 Sept 2017, 21:33

I can't test if that actually works, because I've not been able to edit my games at all today. Online editor is completely dead.

In the case where it's the last element I want to change, it might be good enough. The documentation doesn't say which element list remove actually removes; and I'd assume the - operator behaves in the same way.

If it was an arbitrary element in the list, I think my attempt at code earlier would be the only option :S


K.V.
14 Sept 2017, 21:57

Figured out how to work Join:

game.order = (Split("burger;chicken nuggets;fish sandwich", ";"))
n = ListItem(game.order, ListCount(game.order) - 1)
game.order  = game.order - n + (n + " and fries")
msg ("Your order: " + Join(game.order, ", ") + ".")

Your order: burger, chicken nuggets, fish sandwich and fries.


mrangel, you ask fun questions!


mrangel
14 Sept 2017, 22:25

KV: Does it work right if the initial order was "Beans;eggs;spam;fish;chips;spam;spam;eggs"?
(yeah, silly example)

Oh yay, the server's back up ^_^ I can try it myself.
Or is it? Just says "Loading..."
Better than a cloudflare error; but still not able to do anything.


K.V.
14 Sept 2017, 23:02

Just says "Loading..."

Your order: Beans, spam, fish, chips, spam, spam, eggs, eggs and fries.


mrangel
14 Sept 2017, 23:17

I guess that's a "no", then :(

Might work if you reverse the list before and after; but doesn't look like there's an easy way to do that either.

Or, unless a case comes up where I need to change earlier parts of the list, I could have a separate variable for last_element, and then only add it to the list when I'm sure it's not going to change again. Always a workaround.


K.V.
14 Sept 2017, 23:27
game.order = (Split("Beans;eggs;spam;fish;chips;spam;spam;eggs", ";"))
include = NewStringList ()
foreach (li, game.order) {
  if (not ListContains(include, li)) list add (include, li)
  else {
    msg ("Ordered more than once: " + li)
  }
}
game.order = include
n = ListItem(game.order, ListCount(game.order) - 1)
game.order  = game.order - n + (n + " and fries")
msg ("Your order: " + Join(game.order, ", ") + ".")

Ordered more than once: spam
Ordered more than once: spam
Ordered more than once: eggs
Your order: Beans, eggs, spam, fish, chips and fries.


Now, to make it say '3 orders of spam, 2 orders of eggs...'


Am I understanding what you're wanting to do correctly?


mrangel
14 Sept 2017, 23:36

KV: No, because you've got the items out of order.

This looks like it should work for the example I gave:

//  (assuming we already have a variable order_queue with the items in)
text_order = Join (order_queue, ";")
text_order = text_order + " with fries"
order_queue = Split(text_order, ";")
// and then go off and do something with them

But of course, Join only works on a stringlist. In my real case, it's a list which contains both strings and scripts. I can't easily put them in different lists, because they need to be processed in the order they were queued up.