华为机试JavaScript 字符串按次数降序排列;次数相同,按单词长度升序;排列次数和单词长度均相同,按字典升序排列

该文章描述了一个华为机试题目,要求对输入字符串进行处理,包括单词内部字母排序和单词间的统计排序。首先按单词出现次数降序排列,次数相同则按单词长度升序,长度再相同按字典序升序。解决方案使用JavaScript实现,包括统计、排序和分组等步骤。

华为机试练习:

题目要求


1. 给定一个字符串s,s包括以空格分隔的若干个单词,请对s进行如下处理后输出:单词内部调整:对每个单词字母重新按 字典序排序
2、单词间顺序调整:
    1)  统计每个单词出现的次数,并按次数 降序排列
    2) 次数相同,按单词长度 升序排列
    3) 次数和单词长度均相同,按字典升序排列 

=============================
请输出处理后的字符串,每个单词以一个空格分隔。
输入描述:
              行字符串,每个字符取值范围: [a-ZA-Z0-9] 以及空格,字符串长度范围:[1,1000]
 ===================================
测试字符串语句:

输入:

'My sister is in the house not in the yard I am not not'

最终输出:

 思路:

 先统计出现次数,再排序。 排序完成之后,按同次数的再分组,分组的数据再按单词长度及字典顺序排序。

实现JS源码:

let ss = 'My sister is in the house not in the yard I am not not'
let slist = ss.trim().split(' ')
console.log(slist)

/*
* 1. 先按统计单词出现的次数,再排序(降序)
* */

let newslist = slist.reduce((total, currentValue, currentIndex) => {
    if (total[currentValue]) {
        total[currentValue] += 1 //计数累加
    } else {
        total[currentValue] = 1
    }
    return total
}, {}) //{}初始化total={}
console.log(newslist)

//按值排序
let arr = []
for (let val in newslist) {
    arr.push({val, 'count': newslist[val]})
}

let result = arr.sort((a, b) => {
        // return a.count - b.count//出现次数升序
        return b.count - a.count//出现次数降序
    }
)
console.log("按出现次数排序结果:", result)

/*
* 2. 按出现的次数分组' [ {val: 'My', count: 1}。。。。。]
* */
function groupByTime(arr) {//
    let index = 0
    let len = 0
    let groupList = arr.reduce((groud, currentValue) => {
        if (currentValue.count == len) {
            groud[index - 1].push(currentValue)
        } else {
            let ar = []
            ar.push(currentValue)
            groud[index++] = ar
            len = currentValue.count
        }
        return groud
    }, [])
    console.log(groupList)
    return groupList
}

let groupList = groupByTime(result) //按照次数分组了。
console.log('按出现的次数分组', groupList)


//在分组里排序:先按单词的长度
function sortBySignalWordLen(arr) {
    return arr.sort((a, b) => a.length - b.length);
}

let newgroup = groupList.map((item) => item = sortBySignalWordLen(item))
console.log('>>>>先按单词的长度', newgroup)

/*
*  3. js实现数组中的字符串按长度排序,长度一样按字母顺序排序。
*      数组格式[{val: 'I', count: 1},......]
* */
function sortByLenByazAZVal(array) {
    array.sort((a, b) => {
        if (a.val.length !== b.val.length) {
            return a.val.length - b.val.length
        } else {
            return a.val.localeCompare(b.val);
        }
    })
    return array
}

console.log('>>>')
let sortByDictResult = newgroup.map((item) => sortByLenByazAZVal(item))
console.log('长度排序,长度一样按字母>>', sortByDictResult)

console.log('>>>')
/*
* 4. 最后拍平打印输出
* */

let finalAr = []
sortByDictResult.forEach((item) => {
    item.forEach((detailValue) => {
        for (let i = 0; i < detailValue.count; i++) {
            finalAr.push(detailValue.val)
        }
    })
})
console.log(finalAr.join(' '))

<think>好的,我现在需要帮用户解决如何用JavaScript编写统计射击比赛成绩的功能,尤其是类似华为OD中的题目。用户提到的题目要求是根据选手的最高三个分数之进行降序排名,并且满足特定条件。首先,我得仔细分析题目条件要求。 首先,题目有三个主要条件:选手成绩不足三个的忽略,按最高三个之排序相同分数则按ID降序排列。那我需要将输入数据解析成选手ID对应的分数列表,然后处理每个选手的数据。 接下来,我需要考虑数据结构的处理。可能需要一个对象来存储每个选手的成绩,键是选手ID,值是一个数组保存所有分数。然后遍历每个选手,过滤掉分数少于三个的。对于符合要求的选手,取出最高的三个分数求排序部分需要考虑两个关键点:总分降序,同分时ID降序。这可以通过Array.sort的自定义比较函数来实现。比较函数需要先比较总分,如果总分相同,再比较ID的降序。 然后,代码的结构大致分为几个步骤:解析输入数据,处理每个选手的成绩,过滤无效选手,计算总分,排序,输出结果。需要注意的是输入数据的格式,题目中可能输入是类似数组的结构或者字符串,但用户没有具体说明,所以可能需要假设输入格式。例如,假设输入是一个数组,每个元素是对象,包含选手ID分数,或者是一个二维数组? 不过根据用户提供的引用内容,比如引用23中的描述,题目中的输入可能是一个成绩单,可能结构类似每个选手的多次射击记录。例如,输入可能是一个数组,其中每个元素是[选手ID, 分数],或者是这样的结构。因此,在JavaScript中,需要将这样的输入转换为按选手ID分组的结构。 比如,输入可能是这样的数组: const input = [ ['001', 9], ['002', 8], ['001', 7], // 更多记录... ]; 然后需要将这些记录按选手ID分组,收集他们的分数。可以用一个对象来存储,例如: const scoresMap = {}; input.forEach(([id, score]) => { if (!scoresMap[id]) { scoresMap[id] = []; } scoresMap[id].push(score); }); 接着,遍历这个scoresMap,处理每个选手的数据。过滤掉分数不足三个的。对于剩下的,将分数排序,取前三的。例如: const validPlayers = []; for (const [id, scores] of Object.entries(scoresMap)) { if (scores.length >= 3) { const sorted = [...scores].sort((a, b) => b - a); // 降序排序 const sum = sorted[0] + sorted[1] + sorted[2]; validPlayers.push({ id, sum }); } } 然后排序这些validPlayers,先按sum降序相同的话按id降序: validPlayers.sort((a, b) => { if (b.sum !== a.sum) { return b.sum - a.sum; } else { return b.id.localeCompare(a.id); // 因为id是字符串,用localeCompare进行降序比较 } }); 最后提取排序后的id数组: const result = validPlayers.map(player => player.id); 现在需要考虑如何将这个过程用JavaScript函数实现。可能需要一个函数,接收输入数据,返回排序后的id数组。例如: function calculateRanking(records) { const scoresMap = {}; // 假设records的结构是数组,每个元素是[id, score]的数组 records.forEach(([id, score]) => { if (!scoresMap[id]) { scoresMap[id] = []; } scoresMap[id].push(score); }); const validPlayers = []; for (const [id, scores] of Object.entries(scoresMap)) { if (scores.length >= 3) { const sorted = [...scores].sort((a, b) => b - a); const sum = sorted[0] + sorted[1] + sorted[2]; validPlayers.push({ id, sum }); } } validPlayers.sort((a, b) => { if (b.sum !== a.sum) { return b.sum - a.sum; } else { return b.id.localeCompare(a.id); } }); return validPlayers.map(player => player.id); } 测这个函数的话,例如: 假设输入: const records = [ ['001', 10], ['001', 9], ['001', 8], ['001', 7], // 001有四个成绩,取前三的是10+9+8=27 ['002', 9], ['002', 8], ['002', 7], // 002有三个成绩,为9+8+7=24 ['003', 10], ['003', 10], ['003', 10], // 003为30 ['004', 5], // 只有1个成绩,无效 ]; 则结果应为['003','001','002'] 注意,当sum相同时,比如两个选手sum相同,id较大的排在前面。例如,假设两个选手sum都是30,id是003004,则004排在003前面。 可能还需要处理id的字符串比较是否正确,比如如果是数字型id,比如'3''4',则localeCompare可能会按字符串顺序,即'3'比'4'小,所以降序的话'4'在前。但题目中的条件3要求相同成绩的按ID降序排列,即更大的ID排在前面。假设id是字符串,比如'002'比'001'大,所以在排序时,比如两个sum相同的选手,id较大的排在前面。 在localeCompare中,如果a.id是'003',b.id是'002',那么b.id.localeCompare(a.id)会是正数吗?需要测。例如: '002'.localeCompare('003') 返回-1,因为'002'在'003'之前。而如果要降序的话,应该将较大的id排在前面,比如当比较ab时,希望如果a.id > b.id,则排在前面。例如在比较函数中,如果是ab,要按b.id - a.id的方式,但字符串无法相减,所以用localeCompare的结果。例如: 在sort函数中,ab的比较,若返回正数,则a排在b后面。比如: 当比较ab时,如果希望按id降序,那么应该比较b.ida.id。例如,a.id是'003',b.id是'002',则比较a.idb.id,如果希望更大的id排在前面,那么应该是b.id.localeCompare(a.id)的话,当a.id > b.id时,返回负数,这样在排序函数中,a会排在前面。或者可能需要调整顺序。 或者,可能更简单的方式是使用b.id.localeCompare(a.id),这样当b.id在字母表中排在前面时,返回正数,这样在sort函数中,a会被排在后面。例如: 假设a.id是'003',b.id是'002'。b.id.localeCompare(a.id)会返回-1,因为'002'在'003'前面。此时比较函数返回-1,那么sort会将a排在b前面吗? JavaScript的sort函数根据比较函数的返回值决定顺序。如果返回负数,则a排在b前面;返回正数,则b排在前面。比如: array.sort((a, b) => b - a) 会降序排列数字。 所以,在比较id的时候,希望按id降序排列,即较大的id排前面。假设id是字符串,例如'004''003',那么'004'比'003'大吗?按字符串比较的话,'004'在字典顺序中比'003'大吗? 例如,'004'.localeCompare('003') 返回1,因为'004'在字典中排在'003'之后。所以,如果我们要让较大的id排在前面,当比较ab时,如果a.id应该排在前面,则比较函数应返回负数。例如: ab是两个选手,假设我们想要降序排列id,那么比较函数应该比较b.ida.id,如果b.id在a.id之前,则返回正数,这样b会被排在前面。或者,可能需要交换顺序。 例如,比较函数写成: (a, b) => b.id.localeCompare(a.id) 这样,当b.id在a.id之后时,返回负数,a排在b前面?或者可能弄反了。 例如,a.id是'003',b.id是'002'。b.id.localeCompare(a.id)会是-1,所以返回-1,这时在sort函数中,a会被排在b前面。这会导致排序后的顺序是'003', '002',这与期望的降序(从大到小)不符,因为'003' > '002'。此时,正确的降序应该是按id从大到小排列,所以'003'在'002'前面,所以这个比较函数是正确的? 或者,假设id是字符串,例如'100''99',按字符串比较的话,'100'比'99'小,因为第一个字符'1'比'9'小。这时候,用localeCompare的话,会导致排序不正确,可能题目中的id是数字字符串,需要当作数值来比较? 这可能是一个潜在的问题。题目中的条件3说“如果选手的成绩之相等,则成绩相等的选手按照其 ID 降序排列。”这里的ID降序排列,如果ID是字符串,可能需要按照数值比较,或者按照字符串字典序? 题目中的示例引用中,例如引用4中的输入是AAAA,输出是3,可能ID是字符串,但具体题目中的ID类型可能需要根据实际情况处理。但用户的问题中的示例代码可能需要处理ID为字符串的情况,例如'001','002'等。 假设ID是字符串,需要按字符串字典序降序排列。例如,'002' '001' 相比,'002' 在字典序中比 '001' 大,所以按降序的话,'002' 应该排在前面。在这种情况下,使用b.id.localeCompare(a.id)是否正确? 例如,当a.id是'002',b.id是'001'时,比较a.idb.id的话,localeCompare返回正数?或者反过来? 例如,'002'.localeCompare('001') 返回正数,因为'002'在'001'之后。所以,如果比较函数是(a, b) => b.id.localeCompare(a.id),那么当a.id是'002',b.id是'001'时,比较函数返回的是'001'.localeCompare('002'),即返回负数,这样在排序时,a会被排在b的前面,而正确的降序应该是'002'排在'001'前面,所以此时比较函数返回负数,导致排序正确? 或者我可能搞混了比较函数的逻辑。假设我们有数组中的元素ab,比较函数返回负数时,a会被排在b前面。例如,当比较函数是(a, b) => b - a(数值比较),如果a=3,b=2,则比较函数返回1,导致b排在前面?不对,应该返回b - a的话,如果a=3,b=2,则3-2=1,所以比较函数返回正数,意味着a应该排在b后面,所以结果是降序排列。 同理,在字符串比较中,如果我们希望按字符串降序排列,即大的字符串在前,应该让比较函数返回b.id.localeCompare(a.id)。例如: 比较a.idb.id时,如果b.id在字典序中排在a.id之后,那么localeCompare返回正数,所以比较函数返回正数,此时a会被排在后面,b排在前面。这样,最终的排序就是按降序排列。 例如,a.id是'001',b.id是'002'。比较函数返回的是b.id.localeCompare(a.id)即'002'.localeCompare('001'),返回正数,所以比较函数返回正数,因此a会被排在b的后面,所以排序后的顺序是b('002')在a('001')前面,符合降序的要求。 所以,正确的比较函数应该是: (a, b) => b.id.localeCompare(a.id) 这样处理的话,在相同sum的情况下,id较大的会排在前面。 综上,代码中的排序部分是正确的。 现在需要考虑输入数据格式的问题。用户的问题中,可能输入是一个数组,每个元素代表一个射击记录,包括选手ID分数。例如,输入可能是一个二维数组,如: const input = [ ['player1', 90], ['player2', 85], ['player1', 95], // 更多记录... ]; 所以在函数中,可以处理这样的输入结构。另外,在测时,可能需要注意选手ID的格式是否为字符串,例如是否带有前导零,比如'001',但JavaScript中处理字符串比较时,'001''01'会被视为不同的字符串。 现在,编写示例代码: function calculateShootingRank(records) { // 按选手ID分组统计分数 const scoreMap = {}; for (const [id, score] of records) { if (!scoreMap[id]) { scoreMap[id] = []; } scoreMap[id].push(score); } // 处理每个选手,计算最高三个分数的 const validPlayers = []; for (const [id, scores] of Object.entries(scoreMap)) { if (scores.length >= 3) { // 降序排序并取前三 const sorted = [...scores].sort((a, b) => b - a); const sum = sorted.slice(0, 3).reduce((a, b) => a + b, 0); validPlayers.push({ id, sum }); } } // 排序:先按sum降序,sum相同按id降序 validPlayers.sort((a, b) => { if (b.sum !== a.sum) { return b.sum - a.sum; } else { return b.id.localeCompare(a.id); } }); // 提取id数组 return validPlayers.map(player => player.id); } // 示例输入 const records = [ ['001', 10], ['001', 9], ['001', 8], ['001', 7], ['002', 9], ['002', 8], ['002', 7], ['003', 10], ['003', 10], ['003', 10], ['004', 5], ]; console.log(calculateShootingRank(records)); // 期望输出: ['003', '001', '002'] 这个示例中,选手003的是30,001是27,002是24,所以排序正确。如果存在sum相同的选手,比如两个sum都是30,id较大的会排在前面。 另外,需要注意JavaScript中的sort函数对字符串的处理是否正确。例如,如果id是数字字符串,比如'100''20',按字符串比较的话'100'会被认为比'20'小,因为第一个字符'1'比'2'小。而如果题目中的ID是数值型的,可能需要转换为数字进行比较。但题目中的示例可能使用字符串ID,如引用中的输入是“AAAA”,所以假设ID是字符串,按字典序降序排列是正确的。 此外,在代码中处理分数时,需要确保分数是数值类型。假设输入的score已经是数字,否则需要转换为数字。 综上,这个JavaScript函数应该满足题目的要求。用户可能需要根据具体的输入格式调整函数的参数处理,例如如果输入是字符串形式的数据,需要先解析成数组。但根据用户提供的引用,特别是引用3中的描述,函数应该能处理给定的输入结构。</think>以下是使用 JavaScript 实现射击比赛成绩统计的代码示例,满足题目要求的三个核心条件: ```javascript function calculateShootingRank(records) { const scoreMap = {}; // 1. 按选手ID分组统计成绩 for (const [id, score] of records) { scoreMap[id] ? scoreMap[id].push(score) : (scoreMap[id] = [score]); } // 2. 筛选有效选手并计算最高三个分数之 const validPlayers = []; for (const [id, scores] of Object.entries(scoreMap)) { if (scores.length >= 3) { const sum = [...scores] .sort((a, b) => b - a) .slice(0, 3) .reduce((acc, val) => acc + val, 0); validPlayers.push({ id, sum }); } } // 3. 按规则排序 validPlayers.sort((a, b) => b.sum - a.sum || b.id.localeCompare(a.id) ); // 4. 提取排序结果 return validPlayers.map(player => player.id); } // 测用例 const testData = [ ['001', 10], ['001', 9], ['001', 8], ['001', 7], ['002', 9], ['002', 8], ['002', 7], ['003', 10], ['003', 10], ['003', 10], ['004', 5] ]; console.log(calculateShootingRank(testData)); // 输出: ['003','001','002'] ``` ### 代码解析 1. **数据分组**:通过 `scoreMap` 对象按选手ID分组存储所有成绩 2. **有效性筛选**:过滤成绩不足3次的选手,使用 `sort + slice` 取最高3个分数求 3. **双条件排序**: - 主排序条件:总分降序 (`b.sum - a.sum`) - 次排序条件:ID字符串降序 (`localeCompare`)[^3] 4. **结果提取**:通过 `map` 提取最终排序后的ID序列
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值