Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Morphism over a array ignore element #115

Open
SamuelColacchia opened this issue Aug 8, 2019 · 2 comments
Open

Morphism over a array ignore element #115

SamuelColacchia opened this issue Aug 8, 2019 · 2 comments
Assignees

Comments

@SamuelColacchia
Copy link

Question

I have been messing around with morphism for the past few weeks and thus far it has been a delight to use!

While using it to morph a array of object I encountered a scenario where I would like to remove a object from the array. Is this possible to do in a schema ?

Information

Schema

createSchema({
  sku: orderItem => {
    if (/* This sku looks good return it */) {
        return orderItem.sku;
    } else if (/** This sku is bad */) {
        // we should remove this object
    } else {
        // Something else
    }
  },
  // other data
});

Data

[
    {
        sku: 'goodsku',
        // other data
    },
    {
        sku: 'badsku'
        // other data
    }
]

Current Result

We set the value of sku to something like null and then loop through the array and remove all object that contain a sku: null

Current Result Data

[
    {
        sku: 'goodsku',
        // other data
    },
    {
        sku: 'null'
        // other data
    }
]

Ideal Result

Perhaps export a enum from morphism that signals this object should be removed. Perhaps this could be apart of #85?

Ideal Result Data

[
    {
        sku: 'goodsku',
        // other data
    }
]

Version

morphism: 1.12.0

@emyann emyann self-assigned this Aug 8, 2019
@emyann
Copy link
Member

emyann commented Aug 9, 2019

Hi @SamuelColacchia, so happy you're having a good time using Morphism! 🎉 Thank you for this detailed case 👌🏽

Do you want to strip a specific property, or the entire object ?

As you already mentioned this is not actually possible to skip a complete object during the transformation. #85 will be about to strip a specific key of an object but your suggestion looks a lot like #35 (comment).

I was thinking about some workarounds and ended up with this solution https://repl.it/@yrnd1/morphism-strip-key

interface Source {
  sku: string
}

interface Target {
  sku?: string | null
}
const schema = createSchema<Target, Source>({
  sku: orderItem => {
    if (orderItem.sku === 'good') {
      return orderItem.sku;
    } else if (orderItem.sku === 'bad') {
      return; // returning undefined will skip this property from the final object
    } else {
      return null
    }
  }
}, { undefinedValues: { strip: true } });

const result = morphism(schema, [{ sku: 'good' }, { sku: 'bad' }])
// => [ { sku: 'good' }, {} ]

Using the options of the schema to strip the undefined values let you skip the property from the object, but you'll still end up with an object containing the rest of the data.

I guess your approach is actually to apply a filter after the transformation has been done in order to filter out the objects you don't want ?

If the workaround above along with a filter does not answer to your question, I could think about 2 solutions:

1. Using a constant signaling the object / key should be stripped
When Morphism will encounter the value REMOVE_OBJECT in any of the key of the final object it will strip the object.

import { REMOVE_OBJECT } from 'morphism';

createSchema({
  sku: orderItem => {
    if (/* This sku looks good return it */) {
        return orderItem.sku;
    } else if (/** This sku is bad */) {
        return REMOVE_OBJECT
    } else {
        // Something else
    }
  },
  // other data
});

2. Using a post processing predicate
When Morphism matches a specific predicate against the final object, it will strip it.

createSchema({
  sku: orderItem => {
    if (/* This sku looks good return it */) {
        return orderItem.sku;
    } else if (/** This sku is bad */) {
        return null
    } else {
        // Something else
    }
  }
}, { stripObject: (target) => target.sku === null  });

I kinda feel the second solution is more flexible since it gives the consumer the ability to define on which criteria an object is stripped, but I'm curious about the solution you would pick ? 🙂

@SamuelColacchia
Copy link
Author

Hi @emyann, Thanks for the detailed post and examples!

Do you want to strip a specific property, or the entire object ?

Remove the entire object

I guess your approach is actually to apply a filter after the transformation has been done in order to filter out the objects you don't want ?

Yes, exactly! I have been thinking of helper functions to write to each this goal. My current idea is fairly simple and i think very crude, a recursive function which loops through a given object and if it encounters a REMOVE_OBJECT const value then it removes the current object. If it is in a array it is removed from the array, if it encounters REMOVE_OBJECT in a root object it returns undefined.

I kinda feel the second solution is more flexible since it gives the consumer the ability to define on which criteria an object is stripped, but I'm curious about the solution you would pick ? 🙂

Your 2nd ideal seems brilliant! It provides the most flexibility and it overall very robust. I may have to write a helper function to use with my schemas following that idea.

So to talk about your 2nd idea in practice.

It would be a search predicate which would?

Loop through all values of a given object, going how deep?

(First layer, deep, configurable depth.)

If the predicate returns true then remove the whole object, if in array then removes from array, if in root object does what?

(returns undefined, returns configurable value, throws error.)

Side Thought

This maybe something that is better suited to a seperate issue but what are your thoughts on morphism actions effect other morphism actions or to say it another way action dependency? For example:

createSchema({
  group: async target => {
    const isGroupSpecial: boolean = await checkIfGroupIsSpecial(target.group);
    if (isGroupSpecial) {
      // group is special 😄
      return target.group;
    } else {
      // group is no special 😦
      return 'notspecial group';
    }
  },
  dependsOnGroupValue: target => {
    /**
     * I depend on the result of group I have a few options:
     * 1: I repeat the code that ran in group so i get the same result
     *      - Duplicate code, make function instead
     *      - multiple async calls, maybe cache the data some how?
     * 2: I somehow access the value of group, is this even possible?
     * 3: My value gets set in a 2nd call to morphism
     */
  };
});

Possible solutions

  1. Create a function that you call multiple times for dependent values, i currently do this by create a wrapper class around each schema that declares all the functions a schema can use.
  2. Split the morphing process into multiple stages. So initially a base level schema that depends on nothing, then the next level which depends on every before it and so on.
  3. Define a dependency system in morphism which would allow a action to wait for the result of another action and then proceed.

Solution one and two would require no change to morphism just a maybe creating a examples folder saying hey here is how you do dependent actions. Solution 3 seems to be most robust but also may require alot a work to implement.

What do you think @emyann?

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

No branches or pull requests

2 participants