Source: iterate.js

import { isgeojson, isarrayofobjects } from "./helpers/helpers.js";

/**
 * @function iterate
 * @summary Iterate and apply a function
 * @async
 * @param {object|array} data - A GeoJSON FeatureCollection or an array of objects
 * @param {object} options - Optional parameters
 * @param {function} [options.func = d => d] - A function to apply to each feature.
 * @param {boolean} [options.mutate = false] - Use `true` to update the input data. With false, you create a new object, but the input object remains the same.
 * @returns {object|array} -  A GeoJSON FeatureCollection or an array of objects. (it depends on what you've set as `data`).
 * @example
 * // A simple example. Rename and select fields.
 * await geo.iterate(*a geojson*, {
  func: (d) => ({
    ...d
    properties: { id: d.properties.ISO3, pop: d.properties.population },
  })
})
* // Another example with a geojson and an async function
* await geo.iterate(*a geojson*, {
  func: async (d) => ({
    ...d,
    geometry: await geo.buffer(d.geometry, { dist: d.properties.size })
  })
 */

export async function iterate(data, { func = (d) => d, mutate = false } = {}) {
  let x = data;
  if (isgeojson(x)) {
    if (!mutate) {
      x = JSON.parse(JSON.stringify(data));
    }
    x.features = await Promise.all(
      x.features.map((d) => Promise.resolve(func(d)))
    );
  } else if (isarrayofobjects(x)) {
    x = await Promise.all(x.map((d) => Promise.resolve(func(d))));
    if (mutate) {
      data.splice(0, data.length, ...x);
    }
  }
  return x;
}