1

I wrote a code snippet that extracts the source object keys (which are supposed to be unknown so I've added a non-existing key baz to be extracted. Every element in the array that I extract - I want to add the key from which It was extracted and then flatten the result to get a single array.

I've encountered two major issues while writing the code:

  1. When trying to use S.insert, It always returned an error because the object values (numbers {r:**<<number>>**}) were of different type than the string i've tried to add to the key Section ({'Section': **'foo'**} for example). I ended up mutating the object just for the sake of conveying my intention.
  2. I didn't manage to write a proper map function that would abstract over the Maybe and let me access the variables inside. So I had to use S.maybeToNullable and then re-wrap the value into Maybe.
  3. Is there a better way of expressing any logic written below with sanctuary?

The code snippet:

const S = require('sanctuary');
const source = {
  foo: [{ r: 1 }, { r: 2 }, { r: 3 }],
  bar: [{ r: 4 }, { r: 5 }, { r: 6 }],
}

const result =
  S.pipe([
    S.map(
      prop => {
        const nullable = S.maybeToNullable(S.value(prop)(source))
        return nullable ? S.Just(nullable.map(val => {val['Section'] = prop; return val})) : S.Nothing
      }
    ),
    S.justs,
    S.reduce(xs => x => [...xs, ...x])([])
  ])(['foo', 'bar', 'baz'])
MaxG
  • 1,079
  • 2
  • 13
  • 26

1 Answers1

0

Here is my solution:

const S = require ('sanctuary');

//    source :: StrMap (Array { r :: Integer })
const source = {
  foo: [{r: 1}, {r: 2}, {r: 3}],
  bar: [{r: 4}, {r: 5}, {r: 6}],
  quux: [{r: 7}, {r: 8}],
};

//    result :: Array { r :: Integer, Section :: String }
const result =
S.filter (S.compose (S.flip (S.elem) (['foo', 'bar', 'baz']))
                    (S.prop ('Section')))
         (S.chain (S.pair (s => S.map (S.unchecked.insert ('Section') (s))))
                  (S.pairs (source)));

There are several things to note:

  • S.pipe does not feel natural to me in this case as there are two inputs (source and ['foo', 'bar', 'baz']). Using S.pipe would necessitate hard-coding one of these inputs in the pipeline.

  • S.insert cannot be used for updating records, but S.unchecked.insert can be used for this purpose. I appreciate being able to suppress type checking when an expression I know to be sound is considered unsound by the type checker.

  • It's not clear to me from your question whether output order is important. Your solution respects the order of the array of section names; mine does not. Let me know whether output order is important.

davidchambers
  • 23,918
  • 16
  • 76
  • 105
  • Sorry, It looks much more complicated and unreadable. I come from other languages with FP capabilities and I know for sure that this isn't what I'm looking for. Can you address the issues `1` and `2` I've mentioned? I'd be happy to first solve those problems. – MaxG Nov 18 '19 at 09:35
  • `S.value (prop) (source)` gives a `Maybe (Array { r :: Integer })`. To operate on the `{ r :: Integer }` you will need to map *twice*. Try replacing your lambda with `prop => S.map (S.map (S.unchecked.insert ('Section') (prop))) (S.value (prop) (source))`. – davidchambers Nov 18 '19 at 13:35