-1

Consider this problem:

Create a function zipmap that takes in two sequences, and creates a dictionary from the elements of the first sequence to the elements of the second.

zipmap([1, 2, 3], [4, 5, 6]) => {1: 4, 2: 5, 3: 6}

My solution is below as an answer, can anyone come up with a better way of doing it?

Storms786
  • 416
  • 1
  • 7
  • 20

4 Answers4

2

This is already built into Ramda, as zipObj:

console .log (
  R.zipObj ([1, 2, 3], [4, 5, 6])
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>

And it's also now a language feature, maybe not yet quite widely enough supported, but getting close: Object.fromEntries.

Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103
1
const zipmap = (arr1 ,arr2) => arr1.reduce((p,c,i) => {
  p[c] = arr2[i];
  return p;
},{});
Shalom Peles
  • 2,285
  • 8
  • 21
  • nice one, very neat. Thanks for that.. I didn't realise you could pass the index of an array into reduce – Storms786 Aug 26 '19 at 12:38
1

Here's a simple recursive implementation -

// None : symbol
const None =
  Symbol()

// zipMap : ('k array, 'v array) -> ('k, 'v) object
const zipMap = ([ k = None, ...keys ] = [], [ v = None, ...values ] = []) =>
  k === None || v === None
    ? {}
    : { [k]: v, ...zipMap(keys, values) }

console.log(zipMap([ 1, 2, 3 ], [ 4, 5, 6 ]))
// { 1: 4, 2: 5, 3: 6 }

But it's not much of a "mapping" function; it always returns an Object. What if you wanted a different result?

 
// None : symbol
const None =
  Symbol()

// identity : 'a -> 'a
const identity = x =>
  x

// zipMap : (('k, 'v) -> ('kk, 'vv), 'k array, 'v array) -> ('kk, 'vv) array
const zipMap =
  ( f = identity                 // ('k, v') -> ('kk, 'vv)
  , [ k = None, ...keys ] = []   // 'k array
  , [ v = None, ...values ] = [] // 'v array
  ) =>                           // ('kk, 'vv) array
    k === None || v === None
      ? []
      : [ f ([ k, v ]), ...zipMap(f, keys, values) ]

// result : (number, number) array
const result =
  zipMap
    ( identity
    , [ 1, 2, 3 ]
    , [ 4, 5, 6 ]
    )

console.log(result)
// [ [ 1, 4 ], [ 2, 5 ], [ 3, 6 ] ]

console.log(Object.fromEntries(result))
// { 1: 4, 2: 5, 3: 6 }

// result2 : (number, number) array
const result2 =
  zipMap
    ( ([ k, v ]) => [ k * 10, v * 100 ]
    , [ 1, 2, 3 ]
    , [ 4, 5, 6 ]
    )
    
console.log(Object.fromEntries(result2))
// { 10: 400, 20: 500, 30: 600 }

Instead of creating an Object using Object.fromEntries, you could just as easily create a Map too -

// result2 : (number, number) array
const result2 =
  zipMap
    ( ([ k, v ]) => [ k * 10, v * 100 ]
    , [ 1, 2, 3 ]
    , [ 4, 5, 6 ]
    )

// m : (number, number) map
const m = 
  new Map(result2)

// Map { 10 => 400, 20 => 500, 30 => 600 }
Mulan
  • 129,518
  • 31
  • 228
  • 259
0
const R = require('ramda')

const zipmapSeparate = (...arr) => arr[0].map((zeroEntry, index) => {
    const item = {}
    item[arr[0][index]] = arr[1][index]
    return item
})

const zipmapReduce = (zipmap1) => zipmap1.reduce((accumulator, current) => {
    const key = Object.keys(current)[0]
    const value = Object.values(current)[0]
    accumulator[key]=value
    return accumulator
}, {})

const zipmap = R.compose(zipmapReduce, zipmapSeparate)

console.log(zipmap([1, 2, 3], [4, 5, 6]))
Storms786
  • 416
  • 1
  • 7
  • 20
  • This is a bit sloppy regarding the used target data structure. A plain old Javascript `Object` is an unordered map and thus not well suited for mapping `Array`s. What you need is an ordered `Map`. –  Aug 26 '19 at 12:35
  • Thanks for that bob, I didn't even know there was a `Map` in javascript! By the way, the question is from a list of exercises I'm doing to learn JS and FP. – Storms786 Aug 26 '19 at 12:41
  • @bob: what do you mean by that? Both `Object` and `Map` are ideally unordered collections, but both technically have a an explicit ordering defined. The only logical difference is in the allowed keys. Practically, of course, they offer differing APIs. – Scott Sauyet Aug 26 '19 at 15:00
  • @ScottSauyet AFAIK there is no property order defined for `Object`s in the spec. Maybe it has changed with ES6, I haven't checked that. However, major vendors implement insertion order for a long time, so it is a de facto standard. I just wanted to sensitize the OP on this detail. –  Aug 26 '19 at 17:42
  • 1
    @bob: Yes, it was standardized in ES6 to the existing *de facto* standard of insertion order. I try very hard to write code (especially in libraries) that does not assume this order, not because I think it might one day change, but because it feels like an implementation detail, even if it is in the spec. The whole point of sets and maps is to not worry about order. – Scott Sauyet Aug 26 '19 at 17:45