Splitting an ArrayList for each PVector (0,0) (updated)

Hi there :slight_smile:

I have an ArrayList of PVectors that I want to split into two separate ArrayLists. I have found out that you can split Lists using .sublist, but I have only managed to split my ArrayList into two Lists, which is close (I guess?) but not what I wanted.

How do I either:

  1. Split an ArrayList into two ArrayLists (NOT Lists)?
  2. Convert the Lists to ArrayLists?

Here is my code so far:

import java.util.List;
ArrayList<PVector> vectors = new ArrayList<PVector>();

void setup() {
  vectors.add(new PVector(100, 100));
  vectors.add(new PVector(150, 100));
  vectors.add(new PVector(150, 150));
  
  vectors.add(new PVector(0, 0));
  
  vectors.add(new PVector(300, 100));
  vectors.add(new PVector(350, 100));
  vectors.add(new PVector(350, 150));
  
  
  List<PVector> head = vectors.subList(0,3);
  println(head);
  
  List<PVector> tails = vectors.subList(4,7);
  println(tails);
}
2 Likes

List<PVector> head = vectors.subList(0,3);:

  • final ArrayList<PVector> head = new ArrayList(vectors.subList(0, 3));

In 99% of cases, we can simply declare an ArrayList as just a List and it should work the same:

import java.util.List;
final List<PVector> vectors = new ArrayList<PVector>();
2 Likes

Thanks @GoToLoop :slight_smile:

Additionally I would like to create new separate ArrayList s based on a delimiter (in my case the PVector (0,0) ), and ideally the solution would work in other cases than just this specific one, for instance cases with no or more (0,0) vectors.
For instance, if I have a list of vectors like below, I would like to create subsets, so the first three become a new ArrayList of vectors and the last three become another ArrayList of Vectors, but without having to hard-code the place where they split?

  vectors.add(new PVector(100, 100));
  vectors.add(new PVector(150, 100));
  vectors.add(new PVector(150, 150));

  vectors.add(new PVector(0, 0));

  vectors.add(new PVector(300, 100));
  vectors.add(new PVector(350, 100));
  vectors.add(new PVector(350, 150));
1 Like

Use method indexOf() in order to find the index of an element, so you know where to subList():

  1. Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/util/List.html#indexOf(java.lang.Object)
  2. Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/util/List.html#subList(int,int)
1 Like

Ah, great, that makes sense!

And for cases where I do not know how many delimiters my ArrayList contains, how would I go about it?
Or is there an easy build in way to return all the subLists, something like the way splitTokens does it for strings?
https://processing.org/reference/splitTokens_.html

1 Like

Here is my very clunkly attempt :grimacing:, but I think there must be better ways to do it:

IntList breakPoints;
ArrayList<PVector> vectors = new ArrayList<PVector>();

PVector zeroVector = new PVector(0, 0);

void setup() {
  breakPoints = new IntList();

  vectors.add(new PVector(0, 0));
  vectors.add(new PVector(100, 100));
  vectors.add(new PVector(150, 100));
  vectors.add(new PVector(150, 150));

  vectors.add(new PVector(0, 0));

  vectors.add(new PVector(300, 100));

  vectors.add(new PVector(0, 0));

  vectors.add(new PVector(350, 100));
  
  vectors.add(new PVector(0, 0));
  
  vectors.add(new PVector(350, 150));
  
  vectors.add(new PVector(0, 0));


  //1 find all places to split and store them in breakPoints
  for (int i = 0; i<vectors.size(); i++) {
    if (vectors.get(i).equals(zeroVector)) {
      breakPoints.append(i);
    }
  }

  //This seems to work unless you place two zeroVectors next to each other or have zeroVectors in the beginning or end
  println(breakPoints.size());

  if (breakPoints.size() > 0) {
    int lastBreakpoint = 0;
    for (int i = 0; i<breakPoints.size() + 1; i++) { //2 create breakPoints.size()+1 new ArrayLists 
      ArrayList<PVector> mySubList = new ArrayList();
      if (i == 0) { //first sublist
        mySubList = new ArrayList(vectors.subList(i, breakPoints.get(i)));
        lastBreakpoint = breakPoints.get(i);
        
      } else if (i<breakPoints.size()) { // all middle cases
        mySubList = new ArrayList(vectors.subList(lastBreakpoint+1, breakPoints.get(i)));
        lastBreakpoint = breakPoints.get(i);
      } else { //last sublist
        mySubList = new ArrayList(vectors.subList(lastBreakpoint + 1, vectors.size()));
      }
      println("list " + i + ": " + mySubList);
    } 
  }
}

1 Like

Method indexOf() can only find & stops at the 1st occurrence.

In order to keep going, you’d need to resume searching within a subList() which doesn’t have the previous occurrence.

But from this point on, such workarounds aren’t practical anymore.

Seems like you’ve already come up w/ a solution while I’m typing in here.

But anyways, here’s my attempt I’ve just done for you: :stuck_out_tongue:

/**
 * indicesOf() (v1.0)
 * GoToLoop (2020/Feb/18)
 *
 * https://Discourse.Processing.org/t/
 * splitting-an-arraylist-for-each-pvector-0-0-updated/17883/7
 */

import java.util.List;

final List<PVector> vecs = new ArrayList<PVector>();
final PVector DELIM_VEC = new PVector();

void setup() {
  vecs.add(new PVector(100, 100)); // 0
  vecs.add(new PVector(150, 100)); // 1
  vecs.add(new PVector(150, 150)); // 2
  vecs.add(DELIM_VEC); // 3
  vecs.add(new PVector(300, 100)); // 4
  vecs.add(DELIM_VEC); // 5
  vecs.add(new PVector(350, 100)); // 6
  vecs.add(new PVector(350, 150)); // 7

  println(vecs);

  final int[] delimIndexes = indicesOf(vecs, DELIM_VEC);
  println(delimIndexes); // 3 & 5

  exit();
}

static final <T> int[] indicesOf(final List<T> list, final T object) {
  if (list == null || list.isEmpty())  return new int[0];

  final IntList indices = new IntList();
  final int size = list.size();

  for (int i = 0; i < size; ++i) {
    final T element = list.get(i);
    if (element != null && element.equals(object))  indices.append(i);
  }

  return indices.array();
}
1 Like

Thanks so much @GoToLoop!

And how might I then split my original ArrayList the correct way?

The solution I came up with myself in my previous reply seems really shaky, and also doesn’t really work for cases where two delimiters are placed next to each other or a delimiter is either first or last in the ArrayList?

1 Like

Hello,

Example I worked on the other day:

import java.util.List;

ArrayList<PVector> vectors  = new ArrayList<PVector>();
ArrayList<PVector> vectors1 = new ArrayList<PVector>();
ArrayList<PVector> vectors2 = new ArrayList<PVector>();
ArrayList<PVector> vectors3 = new ArrayList<PVector>();
ArrayList<PVector> vectors4 = new ArrayList<PVector>();

void setup()
  {
  for (int i = 0; i< 10; i++)
    {
    vectors.add(new PVector(i, i));
    }
  
  vectors1 = new ArrayList<PVector> (vectors.subList(0, 5));  
  printArray(vectors1);
  
  vectors2 = new ArrayList<PVector> (vectors.subList(5, 10));  
  printArray(vectors2);

  vectors3 = new ArrayList<PVector> (vectors.subList(5, 6));  
  printArray(vectors3);
  
  vectors4 = new ArrayList<PVector> (vectors.subList(3, 4));  
  printArray(vectors4);
  
  println();
  noLoop();
  }
  
  void draw()
    {
    printArray(vectors1);
    printArray(vectors2);
    printArray(vectors3);
    printArray(vectors4);
    }

I prefer to post references but this was some old code I reworked and quick searches on subject.

:)

1 Like

Okay, let me perhaps try to restate my questions:

Given an ArrayList of any length with any number of occurrences of a delimiter (in my case the PVector (0,0)), how do write a function that returns all the sublists of the original ArrayList?

1 Like

The secret once again is to write more util functions to deal w/ this next challenge step.

I’ve expanded my previous example w/ functions splitListAsList2d() & validateIndices() in addition to indicesOf():

/**
 * indicesOf() & splitListAsList2d() (v2.0.8)
 * GoToLoop (2020/Feb/18)
 *
 * https://Discourse.Processing.org/t/
 * splitting-an-arraylist-for-each-pvector-0-0-updated/17883/11
 */

import java.util.List;
import static java.util.Arrays.binarySearch;

final List<PVector> vecs = new ArrayList<PVector>();
List<List<PVector>> vecs2d;

final PVector DELIM_VEC = new PVector();
final PVector EXTRA_DELIM_VEC = new PVector(EPSILON, TAU, PI);

void setup() {
  vecs.add(new PVector()); // 0
  vecs.add(DELIM_VEC); // 1
  vecs.add(new PVector(100, 100)); // 2
  vecs.add(new PVector(150, 100)); // 3
  vecs.add(new PVector(150, 150)); // 4
  vecs.add(EXTRA_DELIM_VEC); // 5
  vecs.add(new PVector(300, 100)); // 6
  vecs.add(null); // 7
  vecs.add(EXTRA_DELIM_VEC); // 8
  vecs.add(DELIM_VEC); // 9
  vecs.add(new PVector(350, 100)); // 10
  vecs.add(new PVector(350, 150)); // 11
  vecs.add(EXTRA_DELIM_VEC); // 12
  vecs.add(DELIM_VEC); // 13

  println(vecs, vecs.size());

  final int[] delimIndexes = indicesOf(vecs, DELIM_VEC, EXTRA_DELIM_VEC);
  println(join(str(delimIndexes), ", "));

  vecs2d = splitListAsList2d(vecs, delimIndexes);

  for (final List<PVector> vecs1d : vecs2d)  println(vecs1d);
  println(vecs2d.size());

  exit();
}

@SafeVarargs static final int[]
  indicesOf(final List<?> list, final Object... delims) {

  if (list == null || list.isEmpty()) {
    System.err.println("Parameter 'list' is either null or empty!");
    return new int[0];
  }

  final IntList indices = new IntList();
  final int size = list.size();
  final boolean gotDelims = delims != null && delims.length > 0;

  for (int i = 0; i < size; ++i) {
    final Object element = list.get(i);

    if (element == null)  indices.append(i);

    else if (gotDelims)  for (final Object delim : delims)
      if (element.equals(delim)) {
        indices.append(i);
        break;
      }
  }

  return indices.array();
}

@SafeVarargs static final <T> List<List<T>>
  splitListAsList2d(final List<T> list, final int... indices) {

  final List<List<T>> list2d = new ArrayList<List<T>>();

  if (list == null || list.isEmpty()) {
    System.err.println("Parameter 'list' is either null or empty!");
    return list2d;
  }

  final int size = list.size(), lastIdx = size - 1;
  final int[] indexes = _validateIndices(lastIdx, indices);

  if (indexes.length == 0)  return list2d;

  List<T> list1d = null;
  boolean sequenceGotInterrupted = true;

  for (int i = 0; i < size; ++i) {
    if (binarySearch(indexes, i) >= 0) {
      sequenceGotInterrupted = true;
      continue;
    }

    if (sequenceGotInterrupted) {
      list2d.add(list1d = new ArrayList<T>());
      sequenceGotInterrupted = false;
    }

    list1d.add(list.get(i));
  }

  return list2d;
}

@SafeVarargs static final int[]
  _validateIndices(final int maxIndex, final int... indices) {

  if (indices == null || indices.length == 0) {
    System.err.println("Parameter 'indices' is either null or empty!");
    return new int[0];
  }

  final int maxIdx = min(abs(maxIndex), MAX_INT - 3);
  final IntList indexes = new IntList();

  for (final int idx : indices)
    if (idx >= 0 & idx <= maxIdx)  indexes.appendUnique(idx);

  indexes.sort();

  return indexes.array();
}
2 Likes

This is awesome @GoToLoop, thanks so much!

Could I ask you what the line for (final List<PVector> vecs1d : vecs2d) does specifically or how it might look like in a less compact for loop? I am not familiar with that way of writing for loops, so my understanding skips a bit there…

1 Like

for (final List<PVector> vecs1d : vecs2d) println(vecs1d); is equivalent to:

for (int size = vecs2d.size(), i = 0; i < size; ++i) {
  final List<PVector> vecs1d = vecs2d.get(i);
  println(vecs1d);
}

Processing.org/reference/for.html

2 Likes

I ended up converting it to Python Mode as well: :snake:

"""
 * indicesOf() & splitListAsList2d() (v1.0.11)
 * GoToLoop (2020/Feb/21)
 *
 * https://Discourse.Processing.org/t/
 * splitting-an-arraylist-for-each-pvector-0-0-updated/17883/14
"""

from sys import stderr

DELIM_VEC, EXTRA_DELIM_VEC = PVector(), __pvector__(EPSILON, TAU, PI)

vecs = (
    __pvector__(), # 0
    DELIM_VEC, # 1
    PVector(100, 100), # 2
    __pvector__(150, 100), # 3
    PVector(150, 150), # 4
    PVector(EPSILON, TAU, PI), # 5
    PVector(300, 100), # 6
    None, # 7
    EXTRA_DELIM_VEC, # 8
    DELIM_VEC, # 9
    PVector(350, 100), # 10
    PVector(350, 150), # 11
    EXTRA_DELIM_VEC, # 12
    DELIM_VEC # 13
)

print vecs, len(vecs)

def setup():
    delimIndexes = indicesOf(vecs, DELIM_VEC, EXTRA_DELIM_VEC)
    print delimIndexes

    global vecs2d
    vecs2d = splitListAsList2d(vecs, delimIndexes)

    for vecs1d in vecs2d: print vecs1d
    print len(vecs2d)

    exit()


def indicesOf(container, *delims):
    if not container or not _isIterable(container):
        print >> stderr, "Parameter 'container' is empty or non-iterable!"
        return ()

    if len(delims) is 1 and _isIterable(delims[0]): delims = delims[0]

    if _isMappable(container): container = container.values()
    if _isMappable(delims): delims = delims.values()

    indices = []
    gotDelims = not not delims

    for idx, elem in enumerate(container):
        if not elem: indices.append(idx)

        elif gotDelims:
            for delim in delims:
                try:
                    if elem == delim:
                        indices.append(idx)
                        break

                except TypeError:
                    if elem.equals(delim):
                        indices.append(idx)
                        break

    return tuple(indices)


def splitListAsList2d(container, *indices):
    list2d = []

    if not container or not _isIterable(container):
        print >> stderr, "Parameter 'container' is empty or non-iterable!"
        return list2d

    containerSize = len(container)
    indexes = _validateIndices(containerSize - 1, *indices)
    sequenceGotInterrupted = True

    if not indexes: return list2d

    if _isMappable(container): container = container.values()
    elif _isSet(container): container = tuple(container)

    for i in range(containerSize):
        if i in indexes:
            sequenceGotInterrupted = True
            continue

        if sequenceGotInterrupted:
            list1d = []
            list2d.append(list1d)
            sequenceGotInterrupted = False

        list1d.append(container[i])

    return list2d


def _validateIndices(maxIndex, *indices):
    if len(indices) is 1 and _isIterable(indices[0]): indices = indices[0]
    if _isMappable(indices): indices = indices.values()

    if not indices:
        print >> stderr, "Parameter 'indices' is empty!"
        return ()

    maxIdx = min(abs(maxIndex), MAX_INT - 3)
    indexes = []
    errorTypes = TypeError, ValueError

    for idx in indices:
        try: idx = int(idx)
        except errorTypes: continue
        0 <= idx <= maxIdx and idx not in indexes and indexes.append(idx)

    indexes.sort()

    return tuple(indexes)


def _isIterable(obj, PROP='__iter__'): return hasattr(obj, PROP)
def _isMappable(obj, PROP='values'): return hasattr(obj, PROP)
def _isSet(obj, PROP='isdisjoint'): return hasattr(obj, PROP)
1 Like

I am not very good at working with vector arrays and had posted my own question about them a little while ago. I received some really good answers which helped me out quite a bit. Eventually a new problem arrived and I didn’t want to have to post it and wait for a reply; so I just turned the array vector into two separate regular arrays; arraynamex[] and arraynamey[]. Obviously I can still get any result that the array vector offers; although it may be a bit more roundabout - but it works for me. Good luck

1 Like

For completeness’ sake, a p5js flavor version as well: :smile_cat:

/**
 * indicesOf() & splitListAsList2d() (v1.0.4)
 * GoToLoop (2020/Feb/25)
 *
 * https://Discourse.Processing.org/t/
 * splitting-an-arraylist-for-each-pvector-0-0-updated/17883/16
 *
 * https://www.OpenProcessing.org/sketch/847230
 */

'use strict';

const vecs = [];
let vecs2d, delimVec, extraDelimVec;

function setup() {
  noCanvas();

  delimVec = createVector();
  extraDelimVec = new p5.Vector(Math.E, TAU, PI);

  vecs.push(createVector()); // 0
  vecs.push(delimVec); // 1
  vecs.push(createVector(100, 100)); // 2
  vecs.push(new p5.Vector(150, 100)); // 3
  vecs.push(createVector(150, 150)); // 4
  vecs.push(extraDelimVec); // 5
  vecs.push(createVector(300, 100)); // 6
  vecs.push(null); // 7
  vecs.push(extraDelimVec); // 8
  vecs.push(delimVec); // 9
  vecs.push(createVector(350, 100)); // 10
  vecs.push(createVector(350, 150)); // 11
  vecs.push(extraDelimVec); // 12
  vecs.push(delimVec); // 13

  print(vecs);

  const delimIndexes = indicesOf(vecs, // list[]
                                 p5.Vector.prototype.equals, // equals()
                                 delimVec, extraDelimVec); // ...delims[]

  print(delimIndexes);

  vecs2d = splitListAsList2d(vecs, delimIndexes);

  for (const vecs1d of vecs2d)  print(vecs1d);
  for (const vecs1d of vecs2d)  console.table(vecs1d);

  print(vecs2d.length);
}

function indicesOf(list, equals, ...delims) {
  if (!(list && _len(list) && _isIterable(list))) {
    console.warn("Parameter 'list' is empty or non-iterable container!");
    return new Uint32Array;
  }

  if (delims.length == 1 && _isIterable(delims[0]))  delims = delims[0];

  const indices = [],
        gotDelims = !!_len(delims),
        useEquals = typeof equals == 'function';

  var i = 0;

  for (const elem of list.values()) {
    if (!elem)  indices.push(i);

    else if (gotDelims)  for (const delim of delims.values())
      if (useEquals? equals.call(elem, delim) : elem == delim) {
        indices.push(i);
        break;
      }

    ++i;
  }

  return new Uint32Array(indices);
}

function splitListAsList2d(list, ...indices) {
  const list2d = [], size = _len(list), lastIdx = size - 1;

  if (!(list && size && _isIterable(list))) {
    console.warn("Parameter 'list' is empty or non-iterable container!");
    return list2d;
  }

  const indexes = _validateIndices(lastIdx, ...indices);
  if (!indexes.length)  return list2d;

  if (_isHashable(list))  list = [...list.values()];

  for (let list1d, sequenceGotInterrupted = true, i = 0; i < size; ++i) {
    if (indexes.includes(i)) {
      sequenceGotInterrupted = true;
      continue;
    }

    if (sequenceGotInterrupted) {
      list2d.push(list1d = []);
      sequenceGotInterrupted = false;
    }

    list1d.push(list[i]);
  }

  return list2d;
}

function _validateIndices(maxIndex, ...indices) {
  if (indices.length == 1 && _isIterable(indices[0]))  indices = indices[0];

  if (!_len(indices)) {
    console.warn("Parameter 'indices' is an empty container!");
    return new Uint32Array;
  }

  const maxIdx = min(abs(maxIndex), 2**32 - 1), indexes = new Set;

  for (const idx of indices.values())
    idx >= 0 && idx <= maxIdx && indexes.add(parseInt(idx));
 
  return new Uint32Array(indexes).sort();
}

function _len(obj) {
  return abs(parseInt(obj && ('size' in obj? obj.size : obj.length))) || 0;
}

function _isIterable(obj) {
  return obj && typeof obj.values == 'function';
}

function _isHashable(obj) {
  return obj && typeof obj.has == 'function';
}
2 Likes

I follow the discussion with great interest.

I comment on this :

List<List<PVector>> vecs2d;

phhhh…

  1. List is not in the reference afaik, only ArrayList

  2. I find this confusing. Instead I would make a class PVectorList

class PVectorList {

    ArrayList<PVector> = new ArrayList(); 

   // etc. 
}

and before draw() simply

ArrayList<PVectorList> = new ArrayList();

This would give you cleaner more explicit code.

Chrisir

2 Likes