Quiero contar subcadenas de valores de objetos, es decir, todo el objeto contiene una cadena en lugar de una clave igual a una cadena. Un Xpath de trabajo en XSLT sería
count(//v[contains(.,current-grouping-key())])
Pero no puedo resolver esto en javascript.
Intenté lo siguiente:
const obj = [ { v: 'Bla Blu Bli' }, { v: 'Bla Blu Bli' }, { v: 'Bla Blu' }, { v: 'Bla Bli' } ]; const count = obj.reduce( function(sums,entry) { sums[entry.v] = (sums[entry.v] || 0) + 1; return sums; },{}); console.log(count)Pero esto solo cuenta las cadenas exactas. Entonces obtengo:
"Bla Blu Bli": 2, "Bla Blu": 1, "Bla Bli": 1en vez de
"Bla Blu Bli": 2, "Bla Blu": 3, "Bla Bli": 3¿Hay alguna manera de contar las subcadenas en lugar de los valores exactos?
puedes usar eso:
const obj = [ { v: 'Bla Blu Bli' } , { v: 'Bla Blu Bli' } , { v: 'Bla Blu' } , { v: 'Bla Bli' } ]; const counts = obj .map(e=>evsplit(' ').sort((a,b)=>a.localeCompare(b))) .reduce((r,a,_,all)=> { let terms = a.join(' ') if (!r[terms]) r[terms] = all.reduce((c,x)=>c+(a.every(v=>x.includes(v))?1:0),0); return r },{}) console.log( counts ) .as-console-wrapper {max-height: 100% !important;top: 0;} .as-console-row::after {display: none !important;}Esta segunda versión debería ser más rápida.
(usted escribió que tengo más de 100k valores , en un comentario)
Crea una matriz solo de series diferentes, asociada con el número de copias de series idénticas
Y atraviesa esta matriz sumando a esta cantidad las de los otros conjuntos que incluyen los mismos valores,
seleccionando sólo aquellos cuyo tamaño es mayor.
Utilicé elementos Set porque, según el documento, un [set].has(value) es más rápido que un [array].includes(value)
const obj = [ { v: 'Bla Blu Bli' } , { v: 'Bla Bli Blu' } , { v: 'Bla Blu' } , { v: 'Bla Bli' } ]; const counts = obj .reduce((r,o) => // create arr with unique sets with count of copies { let arr = ovsplit(' ') , sam = r.find(x=>(xssize===arr.length) && arr.every(a=>xshas(a)) ) ; if (sam) ++sam.n // + one more copy else r.push({arr, s:new Set(arr), n:1 }) // next step need arr and set to avoid losing time // in conversion operations between array and Set return r },[]) .reduce((c,e,_,all) => { c[e.arr.join(' ')] = en + all.reduce((s,x)=>((xssize > essize && e.arr.every(a=>xshas(a))) ? s + xn : s),0) // try to find includings, only in largest sets return c },{}) console.log( counts ) .as-console-wrapper {max-height: 100% !important;top: 0;} .as-console-row::after {display: none !important;}Debe usar indexOf o similar para ver si existe una subcadena en una cadena.
Ejemplo:
obj = [ { "v": "Bla † Blu † Bli" }, { "v": "Bla † Blu † Bli" }, { "v": "Bla † Blu" } ] const counts = Object.fromEntries( obj.map(({v}) => [v, obj.reduce((acc, el) => { if (el.v.indexOf(v) > -1) acc++; return acc; }, 0)]) ); console.log(counts);