2

This question title is terrible but it's hard to put in words, the following example will hopefully clear things up.

I'm still learning the more advanced functions of Ramda and spent most of my day trying to get this transformation more succinct in Ramda but the ES6 version still feels more readable and short.

I was hoping a Ramda whiz could help to express this function in a better way, using less functions, maybe shorter.

Is there a better way to write this in Ramda or plain JS?
Thank you!

Update: added some extra properties to the objects as the intention is to keep those intact in the new array of objects.

Link to Ramda REPL

// For every type in bundle.types creates a new bundle obj.

const bundles = [
  {
    name: 'banana',
    input: 'src/banana.js',
    dir: 'dist',
    types: ['esm', 'umd']
  },
  {
    name: 'apple',
    input: 'src/apple.js',
    dir: 'dist',
    types: ['umd']
  }
]

/* =>
[
  {
    name: 'banana',
    input: 'src/banana.js',
    dir: 'dist',
    type: 'esm'
  },
  {
    name: 'banana',
    input: 'src/banana.js',
    dir: 'dist',
    type: 'umd'
  },
  {
    name: 'apple',
    input: 'src/apple.js',
    dir: 'dist',
    type: 'umd'
  }
]
*/

let allBundles = R.chain(R.converge(
  R.pipe(R.xprod, R.map(R.mergeAll)),
  [
    R.pipe(R.dissoc('types'), R.of),
    R.pipe(R.prop('types'), R.map(R.objOf('type')))
  ]
), bundles);

console.log('ramda');
console.log(JSON.stringify(allBundles, null, 2));

allBundles = bundles.reduce((acc, b) => {
  return acc.concat(b.types.map((type) => {
    const bundle = { ...b, type };
    delete bundle.types;
    return bundle;
  }));
}, []);

console.log('lamda')
console.log(JSON.stringify(allBundles, null, 2));
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/ramda.min.js"></script>

4 Answers 4

3

This is the perfect place to use R.chain:

const bundles = [ { name:  "banana"
                  , input: "src/banana.js"
                  , dir:   "dist"
                  , types: ["esm", "umd"]
                  }
                , { name:  "apple"
                  , input: "src/apple.js"
                  , dir:   "dist"
                  , types: ["umd"]
                  }
                ];

const unbundle = ({ types, ...rest }) =>
    types.map(type => ({ ...rest, type }));

const unbundleAll = R.chain(unbundle);

console.log(JSON.stringify(unbundleAll(bundles), null, 2));
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/ramda.min.js"></script>

Here's the same thing in vanilla JS:

const bundles = [ { name:  "banana"
                  , input: "src/banana.js"
                  , dir:   "dist"
                  , types: ["esm", "umd"]
                  }
                , { name:  "apple"
                  , input: "src/apple.js"
                  , dir:   "dist"
                  , types: ["umd"]
                  }
                ];

const unbundle = ({ types, ...rest }) =>
    types.map(type => ({ ...rest, type }));

const concatMap = f => xs => [].concat(...xs.map(f));

const unbundleAll = concatMap(unbundle);

console.log(JSON.stringify(unbundleAll(bundles), null, 2));

Hope that helps.

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

3 Comments

Thanks, this is awesome! Only concern is that all the prop names are listed, it would be great if it would automatically include properties that were added later without refactoring the function...
@luwes Edited. Now you don't need to list all the property names.
Wow great use of the object spread to exclude, didn't think of that. Thanks, accepted!
1
const bundles = [
    { name: 'banana', types: ['esm', 'umd'] },
    { name: 'apple', types: ['umd'] }
];

let newArray = [];
bundles.forEach(bundle => {
    bundle.types.map(type => {
      newArray.push({ name: bundle.name, type })
    });
});
console.log(newArray);

This will output =>
[
  {
   "name": "banana",
   "type": "esm"
  },
  {
    "name": "banana",
    "type": "umd"
  },
  {
    "name": "apple",
    "type": "umd"
  }
]

Comments

1

I'm not good in Ramda, but in JS I see potential:

const allBundles = bundles.reduce(
    (acc, { types, ...attrs}) => 
        [...acc, ...types.map((type) => ({...attrs, type }))], []);

const bundles = [
    { name: 'banana', types: ['esm', 'umd'] }, 
    { name: 'apple', types: ['umd'] },
];

console.log('lamda')
console.log(JSON.stringify(allBundles, null, 2));
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/ramda.min.js"></script>

This would have looked slightly better with flaMap.

2 Comments

Thanks @Sylwester, your answer looks good only the goal is to keep all props intact from the object, it could have any number of props. It wasn't so clear first, I added some more props to the objects.
@luwes like this?
0

In JS I would go for:

const bundles = [
    {
        name: 'banana',
        types: ['esm', 'umd']
    },
    {
       name: 'apple',
       types: ['umd']
    }
];

const expanded = (arr) => {
    return arr.reduce((o,n)=>{
     let result = n.types.map((t)=>{
      return {name:n.name, type:t}
     });
     o = [...o,...result];
     return o;
    }, []);
}

Here is the fiddle.

Explanation: What you want is an array, of objects (therefore reduce to collect the result), which are build by a collection built from a map over the types.

I have no knowledge of RamdaJS

From looking at the documentation, a transformation with Ramda using R.Reduce and R.map should be straigthforward.

In order to get the types, you could leverage pluck.

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.