计算整个对象中包含的值子字符串的出现次数

Counting Occurrences Value Substrings contained in entire Object

我想计算对象值的 子字符串 ,即整个对象 包含 一个字符串而不是键 等于 一个字符串。 XSLT 中的工作 Xpath 将是

count(//v[contains(.,current-grouping-key())])

但我无法在 javascript 中解决这个问题。

我尝试了以下方法:

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)

但这只计算确切的字符串。 所以我得到:

"Bla Blu Bli": 2,
"Bla Blu": 1,
"Bla Bli": 1

而不是

 "Bla Blu Bli": 2,
 "Bla Blu": 3,
 "Bla Bli": 3

有没有办法计算子字符串而不是精确值?

您必须使用 indexOf 或类似的方法来查看字符串中是否存在子字符串。

示例:

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);

你可以使用它:

const obj = 
  [ { v: 'Bla Blu Bli' }
  , { v: 'Bla Blu Bli' }
  , { v: 'Bla Blu'     }
  , { v: 'Bla Bli'     }
  ];

const counts = obj
  .map(e=>e.v.split(' ').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;}

第二个版本应该会更快。

(你在评论中写了 I have 100k+ values

它创建一个仅包含不同系列的数组,与相同系列的副本数相关联
并通过将包含相同值的其他集合的数量添加到该数量来遍历该数组,
通过仅选择尺寸较大的那些。

我使用了 Set 元素,因为根据文档,[set].has(value) 比 [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 = o.v.split(' ')
    , sam = r.find(x=>(x.s.size===arr.length) && arr.every(a=>x.s.has(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(' ')] = e.n  
      + all.reduce((s,x)=>((x.s.size > e.s.size && e.arr.every(a=>x.s.has(a))) ? s + x.n : 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;}