5

Is there a more elegant way then this to execute several functions in succession for each item in the array:

type Transform<T> = (o: T) => T;
type Item = { /* properties */ };

transform(input, transformers: Transform<Item>[]) {
  const items: Item[] = getItems(input);
  return items.map(item => {
    let transformed = item;
    tramsformers.forEach(t => transformed = t(transformed));
    return transformed;
  })
}
2
  • 1
    Define "elegant." Commented Aug 27, 2017 at 9:11
  • I don't like that local variable, but forEach() returns void. Not sure how to pass result of a transformation to the next one. Commented Aug 27, 2017 at 9:14

2 Answers 2

6

This is a great use case for reduce:

transform(input, transformers: Transform<Item>[]) {
  const items: Item[] = getItems(input);
  return items.map(item => transformers.reduce((val, transformer) => transformer(val), item));
}

Or perhaps more readably:

transform(input, transformers: Transform<Item>[]) {
  const items: Item[] = getItems(input);
  return items.map(
    item => transformers.reduce(
      (val, transformer) => transformer(val),
      item
    )
  );
}

Live Example:

function getItems(input) {
  return [
    "abcdefg",
    "1234567"
  ];
}
function transform(input, transformers) {
  const items = getItems(input);
  return items.map(item => transformers.reduce((val, transformer) => transformer(val), item));
}
const result = transform("x", [
  v => v.toUpperCase(),
  v => v.substring(1, v.length - 1)
]);
console.log(result);

As Nitzan Tomer points out, we could do away with the items constant:

transform(input, transformers: Transform<Item>[]) {
  return getItems(input).map(
    item => transformers.reduce(
      (val, transformer) => transformer(val),
      item
    )
  );
}

I frequently keep those sorts of things for debugging, but some good debuggers now make it easy to see the return value of functions before they return (Chrome's does), so if you removed it, you could step into getItems to see the items before the map.

Sign up to request clarification or add additional context in comments.

2 Comments

There's also no need for the items const, it can be a one liner without it.
@NitzanTomer: Indeed, it's not necessary, but I don't think that was the focus of the question. (For my own code, I'd probably leave it there to make debugging easier; it'll get optimized out anyway.)
0

Here's a bit more reusable version, based on @T.J.Crowder's answer:

export type Transform<T> = (o: T) => T;
export function pipe<T>(sequence: Transform<T>[] = []) {
  return (item: T) => sequence.reduce((value, next) => next(value), item);
}

transform(input, transformers?) {
  return getItems(input).map( pipe(transformers) );
}

Note that type is inferred from getItems(input) and return type is transform(): Item[].

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.