计算整个对象中包含的值子字符串的出现次数
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;}
我想计算对象值的 子字符串 ,即整个对象 包含 一个字符串而不是键 等于 一个字符串。 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;}