如果满足条件,求和并组合数组中类似的对象元素-Javascript



我有一个这样形状的数组:

[
{
unique: 8,
views: 24,
name: "https://l.instagram.com/",
sec: 5.39,
},
{
unique: 2,
views: 20,
name: "",
sec: 0,
},
{
unique: 2,
views: 5,
name: "https://www.tiktok.com/",
sec: 5.39,
},
{
unique: 4,
views: 3,
name: "https://l.instagram.com",
sec: 2.00,
},
{
unique: 1,
views: 2,
name: "https://www.tiktok.com",
sec: 2.00,
},
];

我正在尝试将相同的referrers和值的总和(视图、唯一等)组合到一个新的数组中。新数组需要根据条件形成,基本上引用者名称需要根据类型进行格式化。例如,所有(";https://l.instagram.com/"quot;l.instagram.com"https://www.instagram.com/"等)推荐人需要被称为Instagram,与其他人一样——YouTube、TikTok等,如果为空,则需要称为"Direct"。这就是我心目中的最终结果:

[
{
unique: 12,
views: 27,
name: "Instagram",
sec: 7.39,
},
{
unique: 2,
views: 20,
name: "Direct",
sec: 0,
},
{
unique: 3,
views: 7,
name: "TikTok",
sec: 7.39,
},
];

我的想法是通过数组进行映射,如果值包括所需的字符串,它将对新对象中的值求和。也许是map或reduce,或者两者结合?感谢任何指导或帮助。提前感谢!

首先,请在将来展示您自己的尝试。如果其他人还没有,我就不会发布解决方案,因为问题本身没有表现出足够的努力。


这方面最大的挑战是简单地将"https://www.tiktok.com"转换为"TikTok"。我的初始解决方案使用正则表达式测试:/tiktok/i .test (name)。Instagram和TikTok的单独应用程序在这里,很明显如何添加其他应用程序。其他一切都相当简单。它看起来像这样:

const groupBy = (fn) => (xs) =>
xs .reduce ((a, x, _, __, k = fn (x)) => ((a [k] = (a [k] || []) .concat (x)), a), {})
const sumOn = (fields) => (xs) =>
Object .fromEntries (fields .map ((field) => [field, xs .reduce ((a, x) => a + x [field], 0)]))
const groupName = ((rules = [
['Instagram', ({name}) => /instagram/i .test (name)],
['TikTok', ({name}) => /tiktok/i .test (name)],
['Direct', () => true] // must come last
]) => (name) => rules .find (([_, test]) => test (name)) [0])()
const convert = (data) => Object .entries (
groupBy (groupName) (data)
) .map (([name, xs]) => ({name, ...sumOn (['unique', 'views', 'sec']) (xs)}))

const data = [{unique: 8, views: 24, name: "https: //l.instagram.com/", sec: 5.39}, {unique: 2, views: 20, name: "", sec: 0}, {unique: 2, views: 5, name: "https: //www.tiktok.com/", sec: 5.39}, {unique: 4, views: 3, name: "https: //l.instagram.com", sec: 2}, {unique: 1, views: 2, name: "https: //www.tiktok.com", sec: 2}]
console .log (convert (data))
.as-console-wrapper {max-height: 100% !important; top: 0}

  • groupBy(现在是Array.prototype扩展的第三阶段建议,因此可能不再需要太久)根据密钥生成函数的结果对数组的元素进行分组。也就是说,

    groupBy ((n) => n % 2 == 0 ? 'even' : 'odd') ([1, 2, 3, 4, 5])
    //=> {odd: [1, 3, 5], even: [2, 4]} 
    
  • sumOn获取一个属性名称列表并返回一个函数,该函数获取一个对象列表并返回具有每个属性名称的新对象,计算结果为每个提供对象的该属性的总和。

    sumOn (['a', 'c']) ([{a: 1, b: 2, c: 3}, {a: 10, b: 20, c: 30}, {a: 100, b: 200, c: 300})
    //=> {a: 111, c: 333}
    
  • groupName就是我们上面讨论的内容。它为Instagram和TikTok硬编码正则表达式,并测试这些字符串是否包含在名称中,如果不匹配,则默认为"Direct"

  • convert是主函数,它只不过是将这些函数组合在一起,使用groupName作为groupBy的密钥生成函数,获取条目,然后将组名与值调用sumOn的结果以及['unique', 'views', 'sec']的密钥组合在一起。

这可能很好,但我正在考虑如何使其更具可配置性,虽然我不会详细介绍,但我确实有一个解决方案,可以让我们直接配置"Instagram""TikTok",并列出我们想要合计的字段。它看起来像这样:

const groupBy = (fn) => (xs) =>
xs .reduce ((a, x, _, __, k = fn (x)) => ((a [k] = (a [k] || []) .concat (x)), a), {})
const sumOn = (fields) => (xs) =>
Object .fromEntries (fields .map ((field) => [field, xs .reduce ((a, x) => a + x [field], 0)]))
const groupName = ((groups, defaultGroup, rules = [
...groups .map (group => [group, ({name}) => new RegExp (group.toLowerCase (), 'i') .test (name)]),
[defaultGroup, () => true]
]) => (name) => rules .find (([_, test]) => test (name)) [0])
const makeConverter = (
{groups, defaultGroup, totals}, 
grouper = groupName (groups, defaultGroup),
totaler = sumOn (totals)
) => (data) => 
Object .entries (groupBy (grouper) (data)) .map (([name, xs]) => ({name, ... totaler (xs)}))
const convert = makeConverter ({
groups: ['Instagram', 'TikTok'], 
defaultGroup: 'Direct', 
totals: ['unique', 'views', 'sec']
}) 
const data = [{unique: 8, views: 24, name: "https: //l.instagram.com/", sec: 5.39}, {unique: 2, views: 20, name: "", sec: 0}, {unique: 2, views: 5, name: "https: //www.tiktok.com/", sec: 5.39}, {unique: 4, views: 3, name: "https: //l.instagram.com", sec: 2}, {unique: 1, views: 2, name: "https: //www.tiktok.com", sec: 2}]
console .log (convert (data))
.as-console-wrapper {max-height: 100% !important; top: 0}

(抱歉,我错过了这个"直接"要求,将修改并附加)

Array.map和所有数组循环方法,每次在数组中迭代一个元素,而不保留以前元素的信息,并且对未来元素视而不见,因此在循环对象时需要一些方法来跟踪以前的对象。

在我提出的snippet解决方案中,我声明了一个中间对象,其中包含您要收集的每个不同组的对象(在您的情况下是tiktok和instagram,如果您的初始数据集非常复杂,您可能需要通过程序提取这些对象,但在本例中,我只是通过眼睛读取它们)。

接下来,使用for-each循环遍历对象的原始数组。对于每个元素(对象),检查.name属性,并尝试提取可以用作引用中间数组中匹配对象的关键字的子字符串。这是通过将.name拆分为一个数组来完成的,只要找到句点(.),就打断名称字符串。因为你的名字都是(something).tiktok

// suppose current element.name is "https://www.tiktok.com";
key = element.name.split(".")[1];
// key now holds 'tiktok';

这个key现在可以用来引用我们之前声明的对象的中间对象中的相关对象,并且可以像这样访问和更新单个属性:

intermediateObjectOfObjects[key].unique += element.unique;

for-each循环的语句被放入条件if块中,以忽略任何未定义名称的语句。

for-each循环结束时,数据现在将位于一个对象的对象中(内部对象将根据需要进行更新)。

因此,最后我们只需要将更新后的对象移动到它们自己的数组中:

// define a new array:
const finalObjectArray=[];
// iterate through the intermediate array, adding each inner object to the new array:
for (const property in intermediateObjectOfObjects) {
finalObjectArray.push(intermediateObjectOfObjects[property]);
} // end for-in object iterations;

最终结果是一个数组,每个命名组包含一个对象,每个对象的原始数组中的值组合在一起。

const arrayOfObjects = [
{
unique: 8,
views: 24,
name: "https://l.instagram.com/",
sec: 5.39,
},
{
unique: 2,
views: 20,
name: "",
sec: 0,
},
{
unique: 2,
views: 5,
name: "https://www.tiktok.com/",
sec: 5.39,
},
{
unique: 4,
views: 3,
name: "https://l.instagram.com",
sec: 2.00,
},
{
unique: 1,
views: 2,
name: "https://www.tiktok.com",
sec: 2.00,
},
];
const intermediateObjectOfObjects = {
tiktok: {
unique: 0,
views: 0,
name: "https://www.tiktok.com",
sec: 0
},
instagram: {
unique: 0,
views: 0,
name: "https://www.instagram.com",
sec: 0
},
direct: {
unique: 0,
views: 0,
name: "direct",
sec: 0
}
} // end intermediate object;
const finalObjectArray=[];

arrayOfObjects.forEach(element => {
key = (element.name.split(".")[1]) ? element.name.split(".")[1] : 'direct' ;
intermediateObjectOfObjects[key].unique += element.unique;
intermediateObjectOfObjects[key].views += element.views;
intermediateObjectOfObjects[key].sec += element.sec;
});

for (const property in intermediateObjectOfObjects) {
finalObjectArray.push(intermediateObjectOfObjects[property]);
} // end for-in object iterations;
console.log(finalObjectArray);

编辑以包含未定义对象名称的"直接"组:

我没有使用if条件来测试名称,而是使用了一个三元运算符来询问";split('.')是否在元素[1]中返回了有效字符串?是这样使用它作为名称,如果不使用"direct"作为名称">

(语法看起来很奇怪,但意思是我上面引用的:

key = (element.name.split(".")[1]) ? element.name.split(".")[1] : 'direct' ;

当然,对象的中间对象也必须修改为包括"直接"作为命名对象;

您可以使用以下各项的组合:

  • CCD_ 25
  • CCD_ 26
  • Object.entries
  • Object.fromEntries

const data = [{unique: 8,views: 24,name: "https://l.instagram.com/",sec: 5.39}, {unique: 2,views: 20,name: "",sec: 0}, {unique: 2,views: 5,name: "https://www.tiktok.com/",sec: 5.39}, {unique: 4,views: 3,name: "https://l.instagram.com",sec: 2.00}, {unique: 1,views: 2,name: "https://www.tiktok.com",sec: 2.00}];
const result = Object.entries( 
data
.map(({name,...rest}) => ({...rest,name:name.split('.').slice(-2)[0] || "direct" }))
.reduce((prev,{name,...rest}) =>
({...prev,[name]:Object.fromEntries(
Object.entries(rest).map(([k,v]) => [k, (prev[name] && prev[name][k] || 0) + v])
)}), {})
)
.map(([name,rest]) => ({name,...rest}));
console.log( result );

在单独的数组中获取名称。迭代该数组并将其与主数组匹配。您也可以尝试使用(.)拆分网站名称,并在is语句中将===更改为includes。类似这样的东西:

let items = [
{
unique: 8,
views: 24,
name: "https://l.instagram.com",
sec: 5.39,
},
{
unique: 2,
views: 20,
name: "",
sec: 0,
},
{
unique: 2,
views: 5,
name: "https://www.tiktok.com",
sec: 5.39,
},
{
unique: 4,
views: 3,
name: "https://l.instagram.com",
sec: 2.00,
},
{
unique: 1,
views: 2,
name: "https://www.tiktok.com",
sec: 2.00,
},
];

var resultArr = [];
var valueArr = [...new Set(items.map(function(item){ return item.name }))];
console.log(valueArr);
valueArr.map((name) => {
let crrItem = {
name: name,
unique: 0,
views: 0,
sec: 0
}
items.map((item) => {
//   console.log(item.name === name);
if(item.name === name){
crrItem.unique += item.unique;
crrItem.views += item.views;
crrItem.sec += item.sec;
//   console.log(crrItem);
}
});
if(crrItem.unique > 0){
resultArr.push(crrItem);
}
});
console.log(resultArr);

相关内容

  • 没有找到相关文章

最新更新