Skip to content

Conversation

@mfedderly
Copy link
Collaborator

@mfedderly mfedderly commented Dec 18, 2025

How do people feel about moving towards iterators for some of the @turf/meta functions? They were actually introduced in ES6 so we are definitely safe syntax to use. I made an example PR and migrated a few instances of featureEach to make a little demo.

Upsides:

We can introduce new iterator methods today and leave the existing methods alone (and potentially deprecate them). Relatedly, they can be written TypeScript-native from the start so we can be mindful of public API signatures and ease-of-typing issues which we're hitting in @turf/meta now.

We no longer have to check return types and break our loops, the caller can control exactly what they want.

function featureEach(geojson, callback) {
  if (geojson.type === "Feature") {
    callback(geojson, 0);
  } else if (geojson.type === "FeatureCollection") {
    for (var i = 0; i < geojson.features.length; i++) {
      if (callback(geojson.features[i], i) === false) break;
    }
  }
}

We can stop creating any (potentially large) intermediate arrays when they aren't needed

function coordAll(geojson) {
  var coords = [];
  coordEach(geojson, function (coord) {
    coords.push(coord);
  });
  return coords;
}

Downsides:

I'm not sure if generators have perf parity with the existing callback situation.

It seems like we only use a small set of iterator methods really frequently: featureEach, coordEach, flattenEach, segmentEach, geomEach. The reducers are mostly just wrappers of these each methods, and could be inlined in a few places instead.

@mfedderly
Copy link
Collaborator Author

cc @stebogit for input.

Do you have thoughts on whether we can remove most of the functionality and just keep just the *Each functions listed above (or potentially turn them into iterator functions as demonstrated in this PR?). There are a lot of issues when trying to properly move @turf/meta to TypeScript, where the idioms are tricky to apply typings to, and it exposed some corner cases within the functions and existing typings which might require a major version to break anyhow.


const y: number[] = [];
featureEach(fc, (feature) => {
for (const { feature } of iterFeatures(fc)) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This need for destructuring is a little annoying, because we have to also carry featureIndex along. But we're using featureIndex in places, and I'd want signature parity with other iterator tools that will also need to bring along their own other indexes as well.

@mfedderly
Copy link
Collaborator Author

It seems like generators are much slower than callbacks (at least in Chrome today), so this is probably a non-starter from a perf standpoint.

From jsbench.me

image

Setup:

const fc = { "type": "FeatureCollection", features: []};
for (let i=0; i<1000; i++) {
	fc.features.push({ type: "Feature", properties: {}, geometry: { "type": "point", coordinates: [Math.random() * 360 - 180, Math.random() * 180 - 90]}});
}

function* iter(fc) {
	for (const f of fc.features) {
		yield f;
	}
}

function cb(fc, c) {
	for (const f of fc.features) {
		c(f);
	}
}

callback version

let i=0;
cb(fc, (f) => i++);

generator version

let i=0;
for (const f of iter(fc)) {
	i++;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants