psychopy.org | Reference | Downloads | Github

.pop() is not a function

URL of experiment: https://run.pavlovia.org/Crible/testmt/html

Description of the problem: Hi, I’m working on a mouse-tracking experiment which works fine in Builder, now trying to test it on Pavlovia. The latest error I got and cannot solve is linked to the pop() function I’m using to randomize the position of text components. Apparently pop() is an existing function in javascript so I guess it must be something else that’s wrong in my code:

#Begin experiment
function shuffle(a) { 
  var j, x, i;
  for (i = a.length - 1; i > 0; i--) {
      j = Math.floor(Math.random() * (i + 1));
      x = a[i];
      a[i] = a[j];
      a[j] = x;
  }
  return a;
}

target_positions = ([0.4, - 0.4] * 5);
shuffle(target_positions);

#Begin routine
target_x = target_positions.pop();
competitor_x = (target_x * (- 1));
thisExp.addData("target_x", target_x);
thisExp.addData("competitor_x", competitor_x);

I’ve only used the auto-translation for that last part of the code. Perhaps the issue comes from earlier in the code, like the bit where the “shuffle” function is defined. Hopefully it’s just a simple syntax issue that someone will catch.

Just a bit of background: the first part defines a “shuffle” function - although I copied this directly from another topic in the forum so not sure if it works for me. Then I use shuffle to randomize the position of two text components (10 trials at the moment), these positions are then assigned to each text component so that screen side is 50/50.

When I launch the experiment, I directly get an error message (after the dialog box) saying TypeError: target_positions.pop is not a function

Thanks a lot already

@lcrible, looks like the issue is with your array multiplication, that sort of element-wise operation does not work with JavaScript, you will end up with a NaN value which does not have a pop method. Instead, you could use the map method to return an array with the new values.

target_positions = [0.4, -0.4].map((val) => val * 5);

Thanks a lot ! This seems to have solved the issue, although I now have new error messages but these are for a different post!

Someone commented on my crib sheet to say (quote edited to match the numbers here):

This is element-wise multiplication, so each element in the array is multiplied by 5. If the goal is to repeat the array, so you have [0.4, -0.4, 0.4, -0.4…] then use:

Array(5).fill([0.4,-0.4]).flat()

Comments?

@wakecarter, that was me, in the original post lcrible needed an array of x vals. I think at the time I interpreted ([0.4, - 0.4] * 5) as the numpy-style element-wise operation, where the map function was appropriate, however if lcrible wanted to increase the number of x positions, then the new suggestion is appropriate (Array(5).fill etc).

1 Like

Thank you both, yes map() doing what I needed so I ended up just having a long list of values without multiplication, but I’ll definitely try it with Array.

Hi @wakecarter and @dvdbridges,
So I tried the Array method but it doesn’t draw any polygon:

target_positions = Array(5).fill([1,(-1)]).flat()
util.shuffle(target_positions);
scale = 0.55;

And then in my polygon components:
$(target_positions[trials.thisN][0]*scale, 0)

I don’t have any error message, the polygons just don’t appear anymore so I can’t click on anything.
Any ideas what went wrong?

You should avoid having a formula in a component. Save the value to a variable like x_pos and use that.

You could add a text component to display x_pos for debugging purposes

I’ve adapted that formula from your dot probe experiment :wink: It was working fine until I tried using Array. I had tried other ways before (with a defined variable) but couldn’t find a working solution.

@lcrible, the array creation is the equivalent of:

target_positions = [1, -1, 1, -1, 1, -1, 1, -1, 1, -1]

Does the task actually run, or is there an error message output in the browser console?.

@dvbridges, the task runs without an error message, but the boxes and words do not show up at all so I can’t click or move to the next trial. It’s as if there was no position defined for these components.
I need to multiply [1, -1], not individual numbers. For the final experiment I will have something like 160 trials, so it would be great if I didn’t have to have a long list of [1, -1] ,[-1, 1] etc. It’s really just a way to make sure that half the trials will show the target on the left, and the other half on the right, but randomize that across items and participants.

@lcrible, are you changing the x position of a single stim, or a pair of stim that would either be on the left or right side of the screen?

@dvbridges Yes it’s a pair ot stims, one is on the left (-1scale) and one on the right (1scale); there’s always a target and a competitor, and the idea is that targets appear an equal number of times on the left and on the right.

Ok, since you want a list of lists, where the nested list is shuffled, try the following instead, which will work with the auto-translate, except that you need to change the shuffle function to util.shuffle (or see Wakefields solution). Use the following Python code in the relevant tabs:

########## Begin Experiment
Array.prototype.append = [].push  # This is required so you can use append in JS

target_positions = []
posIndex = 0  
tempPos = []
for idx in range(5):
    tempPos = [1, -1]
    shuffle(tempPos)
    target_positions .append(tempPos)
    
scale = .55
xPos1 = None
xPos2 = None

########## Begin Routine
xPos1 = target_positions [posIndex][0] * scale
xPos2 = target_positions [posIndex][1] * scale
posIndex += 1

In this example, pass the xPos1 and xPos2 variables to your stimuli that are switching positions. Here is an example in action (5 repetitions).

[Edited] Sorry for the multiple edits…
I now see the boxes online, but I just realized that it’s not 50/50, in my last run I had 7 targets on the right (instead of 5).
I had to change something from your code:

for (var idx = 0, _pj_a = 5; (idx < _pj_a); idx += 1) {
    tempPos = [1, (- 1)];
    util.shuffle(tempPos);
    target_positions.append(tempPos);
}

to

for (var idx = 0, _pj_a = 10; (idx < _pj_a); idx += 1) {
    tempPos = [1, (- 1)];
    util.shuffle(tempPos);
    target_positions.append(tempPos);
}

because otherwise the experiment stopped running after 5 trials. So I guess it’s just creating a random list of 10 pairs of positions, but it’s not guaranteed that I’ll get a 50/50 randomization.

I would basically need to say “create 5 lists”, times 2. I don’t see where that would be in your code, but it’s also because I’m just copying it without really understanding it…

@lcrible, ok yes the example I gave creates a list of 5 lists, where each of the 5 lists is a set of two x positions, 1 or -1, which are shuffled. This randomisation could result in any combination of positions, but as you want 50 / 50, you should go with the original approach - create a single list of x positions, 50 % left (-1) and 50% right (1) positions, shuffle them, but now in addition, for each position taken from the list, invert it to create the second position. E.g.,

Python code

# Begin Experiment
posIndex = 0
targetPos = [1, -1] * 5
shuffle(targetPos)
scale = .55
xPos1 = None
xPos2 = None 

# Begin Routine
xPos1 = targetPos[posIndex] * scale
xPos2 = (targetPos[posIndex] * -1) * scale  # invert the pos
posIndex += 1

JavaScript

// Begin Experiment
posIndex = 0;
targetPos = Array(5).fill([1,(-1)]).flat()
util.shuffle(targetPos);
scale = 0.55;
xPos1 = null;
xPos2 = null;

// Begin Routine
xPos1 = (targetPos[posIndex] * scale);
xPos2 = ((targetPos[posIndex] * (- 1)) * scale); // invert the pos
posIndex += 1;

In this example, I have created 5 pairs of x positions, and so that means 10 trials. E.g., targetPos = [1, -1] * 5 in the Python code. Positioning of the code component in the routine is important, because you want the positions calculated before the position is set by the component. So, move the code component to the top of the routine. I have recreated the online task, so click the link above to see. You can also hopefully download the repo here

Works like a charm, thank you so much for your help and time!