Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

203
Vistas
How to calculate relative frequency of array elements using Ramda.js?

I have an array with repeating values, and I want to get the relative frequency (i.e., proportions) for each repeating value.

It seems natural to me to treat this as a 2-step procedure:

  1. count the occurrences of each value; and
  2. divide that count by the length of the original array.

To accomplish the first step we can use R.countBy() from ramda.js:

const R = require("ramda")

const myLetters = ["a", "a", "a", "a", "b", "b", "c", "c", "c", "c"]
const counted = R.countBy(R.identity)(myLetters)
counted // => gives {"a": 4, "b": 2, "c": 4}

Now the second step would be to divide counted by the length of myLetters:

counted / R.length(myLetters) // obviously this doesn't work because it's not mapped

I'm a bit lost with how to map this correctly. My current clunky solution that I dislike:

// 1. manually calculate the length and store to a variable
const nvals = R.length(myLetters)

// 2. create a custom division function
const divide_by_length = (x) => R.divide(x, nvals)

// 3. map custom function to `counted`
R.map(divide_by_length, counted) // gives {"a": 0.4, "b": 0.2, "c": 0.4}

Although this works, there's gotta be a more straightforward way with ramda to get from counted to {"a": 0.4, "b": 0.2, "c": 0.4}.

about 4 years ago · Juan Pablo Isaza
3 Respuestas
Responde la pregunta

0

I very much like the approach from Ori Drori, using the fact that ap (f, g) //~> (x) => f (x) (g (x)). (chain used for functions is the related chain (f, g) //~> (x) => f (g (x)) (x).)

My initial thought was similar, using instead lift, which lifts a function that operates on values up to become a one that operates on containers of those values. When the containers are functions, it operates something like lift (f) (g, h) //~> (x) => f (g (x), h (x)), although it's more generic, as lift (f) is variadic, as are the functions supplied to it and therefore also the function it generates, e.g. lift (f) (g, h, i, j) //~> (a, b, c) => f (g (a, b, c), h (a, b, c), i (a, b, c), j (a, b, c))

So, very similarly, I wrote:

const frequencies = lift (map) (
  pipe (length, flip (divide)),
  countBy (identity)
) 

const myLetters = ["a", "a", "a", "a", "b", "b", "c", "c", "c", "c"]

console .log (frequencies (myLetters))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js"></script>
<script> const {lift, map, pipe, length, flip, divide, countBy, identity} = R </script>

Nonetheless, it's not clear to me that a point-free approach offers any benefit here. I'm not sure which I prefer, but this non-point-free Ramda is about as readable to my mind:

const frequencies = (letters, total = letters.length) => 
  map (n => n / total) (countBy (identity) (letters) )


const myLetters = ["a", "a", "a", "a", "b", "b", "c", "c", "c", "c"]

console .log (frequencies (myLetters))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js"></script>
<script> const {lift, map, pipe, length, flip, divide, countBy, identity} = R </script>

about 4 years ago · Juan Pablo Isaza Denunciar

0

You need to combine the results of counting the items in the array, with the length of the array.

You can use R.ap as the S combinator by supplying it with 2 functions. The S combinator signature is S = (f, g) => x => f(x)(g(x)), where f and g are functions.

In your case:

f - Create a map curried with divide by the length

g - Create an object of counts

const { ap, pipe, length, divide, __, map, countBy, identity } = R

const fn = ap(
  pipe(length, divide(__), map), // curry a map by divide by length
  countBy(identity), // create the counts
)

const myLetters = ["a", "a", "a", "a", "b", "b", "c", "c", "c", "c"]

const counted = fn(myLetters)

console.log(counted)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js" integrity="sha512-t0vPcE8ynwIFovsylwUuLPIbdhDj6fav2prN9fEu/VYBupsmrmk9x43Hvnt+Mgn2h5YPSJOk7PMo9zIeGedD1A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

about 4 years ago · Juan Pablo Isaza Denunciar

0

I think that @Scott's answer (2nd solution) caused some pennies to drop for me.

If we define two helper functions preemptively for counting and for dividing:

const myCount = R.countBy(R.identity);
const myDivideBy = (divisor, arr) => R.map(elem => elem / divisor); // I was missing this part

Then we could do:

const calcFreq = (arr) => {
    return R.pipe(myCount, myDivideBy(arr.length))(arr)
}

Which is exactly the 2-step procedure I imagined from the beginning: first count, then divide.

calcFreq(myLetters) // gives {"a": 0.4, "b": 0.2, "c": 0.4}

A relevant post: javascript: efficient way to divide an array by a value

about 4 years ago · Juan Pablo Isaza Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda