leetcode----JavaScript 详情题解(4)

目录

2722. 根据 ID 合并两个数组

2723. 添加两个 Promise 对象

2724. 排序方式

2725. 间隔取消

2726. 使用方法链的计算器

2727. 判断对象是否为空

2624. 蜗牛排序 

 2694. 事件发射器


2722. 根据 ID 合并两个数组

现给定两个数组 arr1 和 arr2 ,返回一个新的数组 joinedArray 。两个输入数组中的每个对象都包含一个 id 字段。joinedArray 是一个通过 id 将 arr1 和 arr2 连接而成的数组。joinedArray 的长度应为唯一值 id 的长度。返回的数组应按 id 升序 排序。

如果一个 id 存在于一个数组中但不存在于另一个数组中,则该对象应包含在结果数组中且不进行修改。

如果两个对象共享一个 id ,则它们的属性应进行合并:

  • 如果一个键只存在于一个对象中,则该键值对应该包含在对象中。
  • 如果一个键在两个对象中都包含,则 arr2 中的值应覆盖 arr1 中的值。

示例

输入:
arr1 = [
    {"id": 1, "x": 1},
    {"id": 2, "x": 9}
], 
arr2 = [
    {"id": 3, "x": 5}
]
输出:
[
    {"id": 1, "x": 1},
    {"id": 2, "x": 9},
    {"id": 3, "x": 5}
]
解释:没有共同的 id,因此将 arr1 与 arr2 简单地连接起来。

题解


var join = function (arr1, arr2) {
    // 合并两个数组
    const mergedArray = arr1.concat(arr2);

    // 建立一个用于存储结果的 Map
    const resultMap = new Map();

    // 遍历合并后的数组
    for (const obj of mergedArray) {
        // 获取当前对象的 id
        const id = obj.id;

        // 如果 resultMap 中已经存在当前 id,则合并对象的属性
        if (resultMap.has(id)) {
            const existingObj = resultMap.get(id);
            resultMap.set(id, { ...existingObj, ...obj });
        }
        // 否则,将当前对象作为新的键值对添加到 resultMap 中
        else {
            resultMap.set(id, { ...obj });
        }
    }

    // 将 resultMap 中的对象按 id 升序转换成数组
    const joinedArray = Array.from(resultMap.values()).sort((a, b) => a.id - b.id);

    return joinedArray;
}

这段代码定义了一个函数 join,接受两个数组作为参数 arr1 和 arr2

代码首先通过 arr1.concat(arr2) 将两个数组合并成一个数组 mergedArray

接下来,代码创建了一个用于存储结果的 Map 对象 resultMap

然后,代码通过遍历合并后的数组 mergedArray,获取每个对象的属性 id

如果 resultMap 中已经存在当前 id,则将已存在的对象与当前对象进行属性合并,并将合并后的对象重新存入 resultMap 中。

否则,将当前对象作为新的键值对添加到 resultMap 中。

最后,将 resultMap 中的对象按照 id 进行升序排列,并转换为数组 joinedArray

最后,函数返回 joinedArray

2723. 添加两个 Promise 对象

给定两个 promise 对象 promise1 和 promise2,返回一个新的 promise。promise1 和 promise2 都会被解析为一个数字。返回的 Promise 应该解析为这两个数字的和。

示例

输入:
promise1 = new Promise(resolve => setTimeout(() => resolve(2), 20)), 
promise2 = new Promise(resolve => setTimeout(() => resolve(5), 60))
输出:7
解释:两个输入的 Promise 分别解析为值 2 和 5。返回的 Promise 应该解析为 2 + 5 = 7。返回的 Promise 解析的时间不作为判断条件。

题解

var addTwoPromises = async function (promise1, promise2) {
    return Promise.all([promise1, promise2])
        .then(([num1, num2]) => {
            const sum = num1 + num2;
            return Promise.resolve(sum);
        });
};

这段代码定义了一个名为addTwoPromises的异步函数,它接受两个参数promise1和promise2。函数内部利用Promise.all方法,将这两个参数传入一个数组中,并返回一个新的Promise。在这个Promise被解析后,通过回调函数中的解构赋值将num1和num2分别赋值为数组中的两个元素。然后,通过将num1和num2相加得到sum,并通过Promise.resolve方法返回一个解析后的Promise。最后,这个解析后的Promise的值将作为addTwoPromises函数的返回值。、

2724. 排序方式

给定一个数组 arr 和一个函数 fn,返回一个排序后的数组 sortedArr。你可以假设 fn 只返回数字,并且这些数字决定了 sortedArr 的排序顺序。sortedArr 必须按照 fn 的输出值 升序 排序。

你可以假设对于给定的数组,fn 不会返回重复的数字。

示例

输入:arr = [5, 4, 1, 2, 3], fn = (x) => x
输出:[1, 2, 3, 4, 5]
解释:fn 只是返回传入的数字,因此数组按升序排序。

题解


var sortBy = function (arr, fn) {
    // 使用数组的 sort 方法进行排序
    arr.sort((a, b) => {
        // 根据 fn 函数的返回值进行比较
        const valA = fn(a);
        const valB = fn(b);
        if (valA < valB) {
            return -1;
        } else if (valA > valB) {
            return 1;
        } else {
            return 0;
        }
    });

    // 返回排序后的数组
    return arr;
};

 

这段代码定义了一个名为sortBy的函数,该函数接收两个参数arr和fn。arr是要排序的数组,fn是用于返回比较值的函数。

在sortBy函数内部,使用数组的sort方法对arr进行排序。sort方法接受一个比较函数作为参数,该函数用于确定元素的排序顺序。

比较函数使用fn函数的返回值来进行元素比较。首先,将fn应用于数组中的两个元素a和b,得到valA和valB。然后,根据valA和valB的大小关系进行比较。如果valA小于valB,返回-1,表示a应该在b之前。如果valA大于valB,返回1,表示b应该在a之前。如果valA等于valB,返回0,表示a和b的相对位置不变。

最后,sortBy函数返回排序后的数组arr。

2725. 间隔取消

现给定一个函数 fn,一个参数数组 args 和一个时间间隔 t,返回一个取消函数 cancelFn

函数 fn 应该立即使用 args 调用,并且在每个 t 毫秒内再次调用,直到调用 cancelFn

示例

输入:fn = (x) => x * 2, args = [4], t = 20, cancelT = 110
输出:
[
   {"time": 0, "returned": 8},
   {"time": 20, "returned": 8},
   {"time": 40, "returned": 8},
   {"time": 60, "returned": 8},
   {"time": 80, "returned": 8},
   {"time": 100, "returned": 8}
]
解释: 
const cancel = cancellable(x => x * 2, [4], 20);
setTimeout(cancel, cancelT);
每隔 20ms,调用 fn(4)。
第一次调用 fn 是在 0ms。fn(4) 返回 8。
第二次调用 fn 是在 20ms。fn(4) 返回 8。
第三次调用 fn 是在 40ms。fn(4) 返回 8。
第四次调用 fn 是在 60ms。fn(4) 返回 8。
第五次调用 fn 是在 80ms。fn(4) 返回 8。
第六次调用 fn 是在 100ms。fn(4) 返回 8。
在 t=110ms 时取消。

题解

var cancellable = function (fn, args, t) {
    // 调用初始函数
    fn(...args);
    const intervalId = setInterval(() => {
        fn(...args);
    }, t);

    // 返回取消函数
    const cancelFn = () => {
        clearInterval(intervalId);
    };

    return cancelFn;
};

这段代码定义了一个名为cancellable的函数,它接受三个参数:fn(函数)、args(函数的参数数组)和t(时间间隔)。这段代码的作用是创建一个可取消的定时器。

首先,在函数内部,我们通过调用初始函数fn并传入参数args来立即执行一次函数。

然后,我们使用setInterval函数创建了一个定时器,该定时器会每隔t毫秒调用一次函数fn并传入参数args。

接下来,我们定义了一个匿名函数cancelFn,它的作用是清除之前设定的定时器,通过调用clearInterval函数并传入定时器的ID(intervalId)来实现。

最后,我们将cancelFn函数作为返回值,以便用户调用并取消之前设定的定时器。

所以,这段代码的功能是创建一个可取消的定时器,可以设置定时器的间隔时间,并提供一个函数用于取消定时器。

2726. 使用方法链的计算器

设计一个类 Calculator 。该类应提供加法、减法、乘法、除法和乘方等数学运算功能。同时,它还应支持连续操作的方法链式调用。Calculator 类的构造函数应接受一个数字作为 result 的初始值。

你的 Calculator 类应包含以下方法:

  • add - 将给定的数字 value 与 result 相加,并返回更新后的 Calculator 对象。
  • subtract - 从 result 中减去给定的数字 value ,并返回更新后的 Calculator 对象。
  • multiply - 将 result 乘以给定的数字 value ,并返回更新后的 Calculator 对象。
  • divide - 将 result 除以给定的数字 value ,并返回更新后的 Calculator 对象。如果传入的值为 0 ,则抛出错误 "Division by zero is not allowed" 。
  • power - 计算 result 的幂,指数为给定的数字 value ,并返回更新后的 Calculator 对象。(result = result ^ value )
  • getResult - 返回 result 的值。

结果与实际结果相差在 10-5 范围内的解被认为是正确的。

输入:actions = ["Calculator", "add", "subtract", "getResult"], values = [10, 5, 7]
输出:8
解释:
new Calculator(10).add(5).subtract(7).getResult() // 10 + 5 - 7 = 8

题解

class Calculator {
  constructor(result) {
    this.result = result;
  }

  add(value) {
    this.result += value;
    return this;
  }

  subtract(value) {
    this.result -= value;
    return this;
  }

  multiply(value) {
    this.result *= value;
    return this;
  }

  divide(value) {
    if (value === 0) {
      throw new Error("Division by zero is not allowed");
    }
    this.result /= value;
    return this;
  }

  power(value) {
    this.result = Math.pow(this.result, value);
    return this;
  }

  getResult() {
    return this.result;
  }
}

这段代码定义了一个名为Calculator的类,它具有以下功能:

  • constructor(result): 这是一个构造函数,用于初始化Calculator类的实例并设置result属性的初始值。

  • add(value): 这是一个方法,用于将给定的value添加到result属性的值上,然后返回当前Calculator实例。这使得可以链式调用这个方法。

  • subtract(value): 这是一个方法,用于从result属性的值中减去给定的value,然后返回当前Calculator实例。

  • multiply(value): 这是一个方法,用于将result属性的值乘以给定的value,然后返回当前Calculator实例。

  • divide(value): 这是一个方法,用于将result属性的值除以给定的value,然后返回当前Calculator实例。如果value为0,则抛出一个错误。

  • power(value): 这是一个方法,用于将result属性的值提升到给定的value次方,然后返回当前Calculator实例。

  • getResult(): 这是一个方法,用于返回result属性的当前值。

通过使用这些方法,可以使用Calculator类进行基本的计算操作,并将多个操作链接在一起来获得最终结果。

2727. 判断对象是否为空

给定一个对象或数组,判断它是否为空。

  • 一个空对象不包含任何键值对。
  • 一个空数组不包含任何元素。

你可以假设对象或数组是通过 JSON.parse 解析得到的。

示例

输入:obj = {"x": 5, "y": 42}
输出:false
解释:The object has 2 key-value pairs so it is not empty.

题解

function isEmpty(data) {
  if (Array.isArray(data)) {
    return data.length === 0;
  }

  if (typeof data === 'object' && data !== null) {
    return Object.keys(data).length === 0;
  }

  return false; // 非数组和对象的其他类型不算为空
}

这段代码定义了一个名为isEmpty的函数,该函数判断给定的参数data是否为空。首先,通过使用Array.isArray()方法判断data是否为数组,如果是,则返回data的长度是否为0,即判断数组是否为空。接下来,通过使用typeof运算符判断data的类型是否为对象并且不为null,如果是,则使用Object.keys()方法获取data的所有属性名组成的数组,并判断该数组的长度是否为0,即判断对象是否为空。最后,如果data既不是数组也不是对象,则返回false,表示其他类型的参数不视为空。

2624. 蜗牛排序 

请你编写一段代码为所有数组实现  snail(rowsCount,colsCount) 方法,该方法将 1D 数组转换为以蜗牛排序的模式的 2D 数组。无效的输入值应该输出一个空数组。当 rowsCount * colsCount !==nums.length 时。这个输入被认为是无效的。

蜗牛排序从左上角的单元格开始,从当前数组的第一个值开始。然后,它从上到下遍历第一列,接着移动到右边的下一列,并从下到上遍历它。将这种模式持续下去,每列交替变换遍历方向,直到覆盖整个数组。例如,当给定输入数组  [19, 10, 3, 7, 9, 8, 5, 2, 1, 17, 16, 14, 12, 18, 6, 13, 11, 20, 4, 15] ,当 rowsCount = 5 且 colsCount = 4 时,需要输出矩阵如下图所示。注意,矩阵沿箭头方向对应于原数组中数字的顺序

 示例

输入:
nums = [19, 10, 3, 7, 9, 8, 5, 2, 1, 17, 16, 14, 12, 18, 6, 13, 11, 20, 4, 15]
rowsCount = 5
colsCount = 4
输出:
[
 [19,17,16,15],
 [10,1,14,4],
 [3,2,12,20],
 [7,5,18,11],
 [9,8,6,13]
]

题解 

Array.prototype.snail = function (rowsCount, colsCount) {
    //数组长度是否等于行数乘以列数,若不相等,则返回一个空数组。
    if (this.length !== rowsCount * colsCount) {
        return [];
    }
    const res = [];

    for (let i = 0; i < rowsCount; i++) {
        res.push([]);
    }
    let seq = true; // seq初始值为true表示正向
    let start = 0; //start初始值为0。
    for (let i = 0; i < this.length; i++) {
        res[start].push(this[i]);
        // 正向
         if (seq) {
            if (start === rowsCount - 1) {
                // 若等于,则将seq变量设为false,表示切换到逆向添加;
                seq = false;
            } else {
                // 若不等于,则将start加1。
                start++;
            }
        } else {
            // 逆向
            if (start === 0) {
                // 判断start是否等于0,若等于,则将seq变量设为true表示切换到正向添加;
                seq = true;
            } else {
                // 若不等于,则将start减1
                start--;
            }
        }
    }
    return res;
}

这段代码定义了一个名为snail的数组方法。这个方法接受两个参数,分别是行数rowsCount和列数colsCount。

首先判断调用该方法的数组长度是否等于行数乘以列数,若不相等,则返回一个空数组。

接下来,创建一个空数组res用来保存结果。使用for循环,循环行数次,并在每次循环中向res数组中添加一个空数组,相当于创建了一个行数*列数的二维数组。

接着定义两个变量seq和start,seq初始值为true表示正向,start初始值为0。

使用第二个for循环,循环遍历调用该方法的数组。在每次循环中,将当前元素添加到res数组中的第start行。

根据seq变量的值,判断是正向添加还是逆向添加。若为正向(seq为true),则判断start是否等于行数-1,若等于,则将seq变量设为false,表示切换到逆向添加;若不等于,则将start加1。

若为逆向(seq为false),则判断start是否等于0,若等于,则将seq变量设为true,表示切换到正向添加;若不等于,则将start减1。

最后,返回res数组作为结果。

简单来说,这段代码的作用是将一个一维数组按照螺旋状排列成一个二维数组,并返回这个二维数组作为结果。

 2694. 事件发射器

设计一个 EventEmitter 类。这个接口与 Node.js 或 DOM 的 Event Target 接口相似,但有一些差异。EventEmitter 应该允许订阅事件和触发事件。

你的 EventEmitter 类应该有以下两个方法:

  • subscribe - 这个方法接收两个参数:一个作为字符串的事件名和一个回调函数。当事件被触发时,这个回调函数将被调用。 一个事件应该能够有多个监听器。当触发带有多个回调函数的事件时,应按照订阅的顺序依次调用每个回调函数。应返回一个结果数组。你可以假设传递给 subscribe 的回调函数都不是引用相同的。 subscribe 方法还应返回一个对象,其中包含一个 unsubscribe 方法,使用户可以取消订阅。当调用 unsubscribe 方法时,回调函数应该从订阅列表中删除,并返回 undefined。
  • emit - 这个方法接收两个参数:一个作为字符串的事件名和一个可选的参数数组,这些参数将传递给回调函数。如果没有订阅给定事件的回调函数,则返回一个空数组。否则,按照它们被订阅的顺序返回所有回调函数调用的结果数组。

示例 

输入:actions = ["EventEmitter", "emit", "subscribe", "subscribe", "emit"], values = [[], ["firstEvent", "function cb1() { return 5; }"],  ["firstEvent", "function cb1() { return 5; }"], ["firstEvent"]]
输出:[[],["emitted",[]],["subscribed"],["subscribed"],["emitted",[5,6]]]
解释:
const emitter = new EventEmitter();
emitter.emit("firstEvent"); // [], 还没有订阅任何回调函数
emitter.subscribe("firstEvent", function cb1() { return 5; });
emitter.subscribe("firstEvent", function cb2() { return 6; });
emitter.emit("firstEvent"); // [5, 6], 返回 cb1 和 cb2 的输出

题解

class EventEmitter {
    constructor() {
        this.events = {}; // {event: [cb1, cb2]}, 存储事件和回调
    }
    subscribe(event, cb) {
        if (!this.events[event]) this.events[event] = []; // 初始化
        this.events[event].push(cb); // 订阅, 将回调存入事件对应数组
        return {
            unsubscribe: () => {
                // 取消订阅
                this.events[event] = this.events[event].filter((e) => e !== cb); // 过滤掉当前回调
            },
        };
    }

    emit(event, args = []) {
        if (!this.events[event]) return []; // 未订阅, 返回空数组
        return this.events[event].map((cb) => cb(...args)); // 执行回调
    }
}

这段代码定义了一个 EventEmiter 类,用于实现事件的订阅和触发功能。

EventEmitter 类的构造函数初始化了一个空的 events 对象,用于存储事件和回调的对应关系。

subscribe 方法用于订阅事件,接受两个参数:event 表示要订阅的事件名称,cb 表示事件触发时要执行的回调函数。如果 events 对象中没有对应的事件数组,会先进行初始化。然后将回调函数添加到事件对应的数组中,并返回一个包含 unsubscribe 方法的对象,用于取消订阅。unsubscribe 方法会将当前回调从事件的数组中过滤掉。

emit 方法用于触发事件,接受两个参数:event 表示要触发的事件名称,args 表示传递给回调函数的参数,默认为空数组。如果 events 对象中没有对应的事件数组,表示该事件未被订阅,直接返回空数组。否则,将事件对应的数组中的每一个回调函数都执行,并传入 args 参数,最后将执行结果组成的数组返回。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值