简体   繁体   English

如何将数组转换为具有每个键的特定名称的JavaScript对象

[英]How to convert an array into a JavaScript object with specific names for each key

I'm trying use JavaScript to convert the following array into a JavaScript object but I don't know how to do it. 我正在尝试使用JavaScript将以下数组转换为JavaScript对象,但是我不知道该怎么做。

Here's my input array. 这是我的输入数组。 For this array each key represents a day of the week (Sunday to Saturday). 对于此数组,每个键代表一周中的一天(星期日至星期六)。 So key 0 = Sunday, key 1 = Monday ....all the way to key 6 which = Saturday. 因此,键0 =周日,键1 =星期一....一直到键6,即周六。

var times = [["8:30-12:00","14:00-18:00"],
["6:15-9:30","13:00-16:00","20:00-23:15"],[],["9:00-21:00"],
["9:00-21:00"],[],[]];

This is the JavaScript object I'd like to convert the above times array to: 这是我想将上述times数组转换为的JavaScript对象:

timeObj = {
  sunday: [
    {
      start: '08:30',
      stop: '12:00'
    },
    {
      start: '14:00',
      stop: '18:00'
    }
  ],
  monday: [
    {
      start: '06:15',
      stop: '9:30'
    },
    {
      start: '13:00',
      stop: '16:00'
    },
    {
      start: '20:00',
      stop: '23:15'
    }
  ],
  tuesday: [],
  wednesday: [
    {
       start: '9:00',
       stop: '21:00'
    }
  ],
  thursday:  [
    {
       start: '9:00',
       stop: '21:00'
    }
  ],
  friday:  [],
  saturday:  []
};

What's the best way to convert the array times into the object timeObj ? 将数组times转换为对象timeObj的最佳方法是什么?

You may create an array of corresponding days and loop over it. 您可以创建一个对应天的数组并在其上循环。 In each iteration, create the day's key in result object whose value will be the start and end time. 在每次迭代中,在结果对象中创建日期的键,其值将是开始时间和结束时间。

 var times = [["8:30-12:00","14:00-18:00"],["6:15-9:30","13:00-16:00","20:00-23:15"],[],["9:00-21:00"],["9:00-21:00"],[],[]]; var days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday']; var result = {}; days.forEach((day, index) => { result[day] = times[index] .map(item => { let [start, stop] = item.split('-'); return {start, stop}; }); }); console.log(result); 

My suggestion is to use Array.prototype.reduce for 1-level list and Array.prototype.map for 2-level lists to create appropriate object of arrays: 我的建议是将Array.prototype.reduce用于1级列表,将Array.prototype.map用于2级列表来创建适当的数组对象:

const data = [["8:30-12:00","14:00-18:00"],["6:15-9:30","13:00-16:00","20:00-23:15"],[],["9:00-21:00"],["9:00-21:00"],[],[]];
const days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];

const result = data.reduce((acc, item, index) => { 
  acc[days[index]] = 
    item.map(day => ({ 
      start: day.substr(0, day.indexOf('-')), 
      end: day.substring(day.indexOf('-') + 1)
    }));
  return acc;
 }, {});

Update 更新资料

As this post is extremely long and goes into the software design and mathematics of the solution, I've posted the final answers here at the top 由于这篇文章非常长,并且涉及解决方案的软件设计和数学,因此我在此处顶部发布了最终答案。

Recommended Answer (human readable): 推荐答案 (可读):

var times = [["8:30-12:00","14:00-18:00"],["6:15-9:30","13:00-16:00","20:00-23:15"],[],["9:00-21:00"],["9:00-21:00"],[],[]];
var days = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];

var timeObj = {};

for (var i = 0; i < times.length; i++)
{
    // Create an empty array:
    timeObj[days[i]] = [];

    // For each timespan in the original array, add it to this day
    for (var j = 0; j < times[i].length; j++)
    {
        var timespan = times[i][j];

        // Parse the string
        var timespanArr = timespan.split("-");
        var timespanObj = {
            // I took the liberty of converting "6:15" to "06:15" for consistency
            // This extra step is NOT taken in the "smallest" answer below
            start: ("0" + timespanArr[0]).substr(-5),
            end: ("0" + timespanArr[1]).substr(-5)
        };

        // Append
        timeObj[days[i]].push(timespanObj);
    }
}

console.log(timeObj);

Smallest Answer (best I could do): 最小答案 (我能做到的最好):

var times = [["8:30-12:00","14:00-18:00"],["6:15-9:30","13:00-16:00","20:00-23:15"],[],["9:00-21:00"],["9:00-21:00"],[],[]];
var start, end;
var obj = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"].reduce((acc, el, idx) => (
    acc[el] = times[idx].map(timespan => (
        [start, end] = timespan.split("-"), {start, end}
    )), acc
), {});

The Problem 问题

So you're actually trying to solve two distinct problems here. 因此,您实际上是在尝试解决两个不同的问题。

  • You're converting indexes to keys. 您正在将索引转换为键。 This is a mapping problem. 这是一个映射问题。 You're actually doing this twice: 您实际上执行了两次:
    • 0 -> sunday , 1 -> monday , etc. 0 -> sunday1 -> monday1 -> monday
    • 0 -> start , 1 -> end 0 -> start1 -> end
  • You're parsing a string: "8:30-12:00" -> ["08:30", "12:00"] 您正在解析一个字符串: "8:30-12:00" -> ["08:30", "12:00"]

Let's write code to solve each of these problems individually, simplify each solution, then combine them. 让我们编写代码来分别解决每个问题,简化每个解决方案,然后将它们组合起来。

Also, I'll give you one piece of handy advice that I hope you carry with you in your programming career: 另外,我将为您提供一些方便的建议,希望您在编程生涯中随身携带:

Always write code for humans first 始终先为人类编写代码

While there may exist some fancy one-liner solution to your problem, if you can't glance at the code and understand what it's doing then you're just causing headaches for future developers who have to maintain your code. 尽管可能存在一些花哨的单线解决方案来解决您的问题,但如果您不看代码并了解其功能,那么只会给将来必须维护代码的开发人员带来麻烦。 It's better to write 20 lines of obvious and simple code than 1 line of garbage that made sense to your last June but now even you don't remember how it works. 最好写20行明显且简单的代码,而不是去年6月有意义的1行垃圾,但现在甚至您都不记得它是如何工作的。

Problem 1: Converting indexes to keys 问题1:将索引转换为键

The most straight-forward way to do this is something like the following: 最简单的方法如下所示:

var arr = [1, 2, 3, 4, 5, 6, 7];
var obj = {
    sunday: arr[0],
    monday: arr[1],
    tuesday: arr[2],
    ...
}

Simplifying (part 1) 简化(第1部分)

While not technically impressive, it does work. 尽管在技术上并不令人印象深刻,但它确实可以工作。 We can automate some of this task if we have some structure that maps the existing indexes to the desired keys that we can iterate over. 如果我们具有某种结构,可以将现有索引映射到可以迭代的所需键,则可以自动执行某些任务。 So what we want is a structure that: 所以我们想要的是一个结构:

  1. Maps a positional integer index to a string key name 将位置整数索引映射到字符串键名
  2. Can be iterated 可以迭代

This is the exact definition of an array. 这是数组的确切定义。 So we can simplify our code using an array of key names: 因此,我们可以使用键名数组简化代码:

var arr = [1, 2, 3, 4, 5, 6, 7];
var days = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];
var obj = {};
for (var i = 0; i < days.length; i++)
{
    obj[days[i]] = arr[i];
}

Neat! 整齐!

Simplifying (part 2) 简化(第2部分)

Taking an array of multiple objects and converting it to a single object is known as reducing . 将多个对象组成的数组并将其转换为单个对象称为reduce This is generally used for things like computing a sum (take an array of numbers and reduce it down to a single number which represents the total of all numbers in the array). 通常用于计算总和(取一个数字数组并将其缩减为一个代表该数组中所有数字的总和的数字)。 However since our output is a single javascript object, we can take advantage of reducing to construct our object. 但是,由于我们的输出是单个javascript对象,因此我们可以利用reduce来构造我们的对象。

The reduce function takes two parameters. reduce函数采用两个参数。 The first is a function (which we'll get to shortly) and the second is the starting value of what's called the "accumulator". 第一个是函数(稍后将介绍),第二个是所谓的“累加器”的起始值 This makes a lot of sense when computing the sum of an array. 在计算数组的总和时,这很有意义。 You have a starting value of zero, and the function takes the current value (the value of the accumulator) and adds the current element to it. 您的起始值为零,该函数采用当前值(累加器的值)并将当前元素添加到该值。 The new accumulator is then passed forward to the next step in the reduction. 然后,新的累加器将前进到还原的下一步。

The function passed as the first parameter has, itself, three parameters. 作为第一个参数传递的函数本身具有三个参数。 Its signature looks like this: 其签名如下所示:

function( accumulator, element, index ) {
    // ...
}

This function will be called once for each element in the array. 该函数将为数组中的每个元素调用一次。 The first value is a "running sum", of sorts. 第一个值是“运行总和”。 The second value will equal whichever element of the array we're currently on. 第二个值将等于我们当前所在的数组的任何元素。 The third value tells you which array element this is. 第三个值告诉您这是哪个数组元素。 Here's code to explain what reduce does: 以下代码说明了reduce的作用:

var arr = [1, 2, 3, 4, 5, 6, 7];
// arr.reduce(myFunction, 0):
var _accumulator = 0;
for (var _i = 0; _i < arr.length; _i++)
{
    _accumulator = myFunction(_accumulator, arr[_i], _i);
}

Simple, right? 简单吧? In fact, this for-loop structure looks a lot like our previous solution. 实际上,这种for循环结构看起来很像我们之前的解决方案。 So let's make them look identical: 因此,让它们看起来相同:

var arr = [1, 2, 3, 4, 5, 6, 7];
var days = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];
var _accumulator = {};
for (var _i = 0; _i < arr.length; _i++)
{
    _accumulator = (
        // Some function that returns _accumulator, but
        // with the days[_i] key set to arr[_i]
    );
}

This makes it more clear that a reducer can do what we want. 这使得减速器可以做我们想要的事情。 In fact, the reducer in question should look like so: 实际上,所讨论的减速器应如下所示:

// Some function that returns _accumulator, but
// with the days[_i] key set to arr[_i]
function setKeyValue(_accumulator, _i)
{
    _accumulator[days[_i]] = arr[_i];
    return _accumulator;
}

Writing this using Array.reduce syntax, we get: 使用Array.reduce语法编写此代码,我们得到:

var arr = [1, 2, 3, 4, 5, 6, 7];
var days = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];
var obj = arr.reduce(
    function(acc, el, idx)
    {
        acc[days[idx]] = arr[idx];
        return acc;
    },
    // The initial value:
    {}
);

Of course if you find reducers difficult to understand you don't need to go this far. 当然,如果您发现减速器难以理解,则无需走太远。 Our previous for-loop solution was good enough. 我们之前的for循环解决方案就足够了。

Problem 2: Parsing a string 问题2:解析字符串

Given the string "8:30-12:00" you want the object: {start: "08:30", end: "12:00"} 给定字符串"8:30-12:00"您需要对象: {start: "08:30", end: "12:00"}

Now there's a lot to be said about checking for bad data. 关于检查不良数据,现在有很多话要说。 What would you do if someone put in the string "8:30+12:00" ? 如果有人输入字符串"8:30+12:00"您会怎么做? What if they put in "8:30-12:00-2:00" ? 如果他们输入"8:30-12:00-2:00"怎么办?

But we'll ignore those problems for now and focus on the task at hand. 但是我们暂时将忽略这些问题,而将精力集中在手头的任务上。 We have two distinct values ("start" and "end") which are found in the string, so we need to split our string. 我们在字符串中找到两个不同的值(“ start”和“ end”),因此我们需要split字符串。 Fortunately our values are separated by a hyphen - so we can split on the hyphen! 幸运的是,我们的值由连字符分隔-因此我们可以split连字符!

var str = "8:30-12:00";
var arr = str.split("-");
console.log(arr);
// Outputs: ["8:30", "12:00"]

Now we're back to problem 1: Mapping the indexes to keys ( 0 -> start , 1 -> end ). 现在回到问题1:将索引映射到键( 0 -> start1 -> end )。 Since we only have two indexes, the for-loop solution may be overkill. 由于我们只有两个索引,因此for循环解决方案可能会显得过大。 So let's just use the initial solution of manually setting them: 因此,让我们使用手动设置它们的初始解决方案:

var arr = str.split("-");
var obj = {
    start: arr[0],
    end: arr[1]
};

There's one final thing I noticed, though. 不过,我注意到了最后一件事 You displayed the string as 08:30 instead of 8:30 in your final answer. 您在最终答案中将字符串显示为08:30而不是8:30 Once again, we'll go with an easy solution: stick some zeroes on the front and remove them if there are too many: 再一次,我们将提供一个简单的解决方案:在前面粘贴一些零,如果有太多则将其删除:

var str = "12:30"; // 12:30
str = "0" + str; // 012:30
// Grab the last 5 characters
str = str.substr(-5); // 12:30 (again)

Combining the above two blocks of code (splitting the string, creating an object, and appending the 0 ) we get: 结合以上两个代码块(拆分字符串,创建对象并附加0 ),我们得到:

var arr = str.split("-");
var obj = {
    start: ("0" + arr[0]).substr(-5),
    end: ("0" + arr[1]).substr(-5)
};

Now that was the final thing regarding parsing the string, but it's not the final thing regarding what code you need to write. 现在,这是解析字符串的最后一件事,但不是您需要编写什么代码的最后一件事。 Because you're not parsing one string. 因为您没有解析一个字符串。 You have an array of them. 你有他们的数组 Meaning you need code like this: 意味着您需要这样的代码:

var arr = ["8:30-12:00", "14:00-18:00"];
var parsedArr = [];
for (var i = 0; i < arr.length; i++)
{
    var splitStr = arr[i].split("-");
    parsedArr[i] = {
        start: ("0" + splitStr[0]).substr(-5),
        end: ("0" + splitStr[1]).substr(-5)
    }
}

Simplifying (part 3) 简化(第3部分)

Just as you can use reduce to convert an array of elements into a single object, there's another function that converts an array of elements into an array of different elements. 正如您可以使用reduce将元素数组转换为单个对象一样,还有另一个函数将元素数组转换为不同元素的数组。 It's known as map . 它被称为map You can use map to, for instance, double every number in an array. 例如,您可以使用map来将数组中的每个数字加倍。

In our case, we want to convert every string to a parsed object. 在我们的例子中,我们希望将每个字符串都转换为已解析的对象。 The result would be something like this: 结果将是这样的:

var arr = ["8:30-12:00", "14:00-18:00"];
var parsedArr = arr.map(function(el)
{
    var splitStr = el.split("-");
    return {
        start: ("0" + splitStr[0]).substr(-5),
        end: ("0" + splitStr[1]).substr(-5)
    };
});

It's not that much shorter than the for-loop version, but it does shave off one line of code at least. 它比for循环的版本不是要短得多,但它至少剃掉的一行代码。

Putting it together 把它放在一起

This was a bit long-winded, but we can now combine our answers to write human-friendly code that accomplishes your goal: 这有点麻烦,但是我们现在可以结合我们的答案来编写实现您目标的人性化代码:

var times = [["8:30-12:00","14:00-18:00"],["6:15-9:30","13:00-16:00","20:00-23:15"],[],["9:00-21:00"],["9:00-21:00"],[],[]];
var days = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];

var timeObj = {};

for (var i = 0; i < times.length; i++)
{
    // Create an empty array:
    timeObj[days[i]] = [];

    // For each timespan in the original array, add it to this day
    for (var j = 0; j < times[i].length; j++)
    {
        var timespan = times[i][j];

        // Parse the string
        var timespanArr = timespan.split("-");
        var timespanObj = {
            start: ("0" + timespanArr[0]).substr(-5),
            end: ("0" + timespanArr[1]).substr(-5)
        };

        // Append
        timeObj[days[i]].push(timespanObj);
    }
}

console.log(timeObj);

This has 13 meaningful lines of code (28 if you count whitespace, comments, and closing brackets) but it took very little effort to write, takes very little effort to read, and it's obvious what the code does with a minimal amount of reading. 它有13条有意义的代码行(如果您计算空格,注释和右括号,则为28行),但是编写起来却花了很少的力气,阅读起来也花了力气,很明显,在最少的阅读量下代码是做什么的。

Now if you feel comfortable with the reduce and map functions then we can take advantage of the "Simplifying (part 2)" and "Simplifying (part 3)" sections to write something like: 现在,如果您对reducemap函数感到满意,那么我们可以利用“简化(第2部分)”和“简化(第3部分)”部分来编写以下内容:

var times = [["8:30-12:00","14:00-18:00"],["6:15-9:30","13:00-16:00","20:00-23:15"],[],["9:00-21:00"],["9:00-21:00"],[],[]];
var days = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];
var obj = times.reduce(function(acc, el, idx)
{
    acc[days[idx]] = el.map(function(timespan)
    {
        var splitStr = timespan.split("-");
        return {
            start: ("0" + splitStr[0]).substr(-5),
            end: ("0" + splitStr[1]).substr(-5)
        };
    });
    return acc;
}, {});

Without a clear understanding of exactly how reduce and map function this code is almost impossible to understand at a glance, and even with that understanding it takes some thinking and struggling to wrap your head around it. 如果没有清楚地了解reducemap函数的确切功能,几乎一眼就很难理解该代码,即使有了这种理解,也需要花费一些精力和精力来解决它。

That's not to say that it's a bad answer. 这并不是说这是一个错误的答案。 Sometimes you're trying to optimize for code size. 有时您正在尝试优化代码大小。 If you need as few kilobytes as possible to improve page loads, then taking advantage of reduce and map cuts the code down to 1/2 the final size. 如果您需要尽可能少的字节来改善页面加载,则可以利用reducemap将代码缩减为最终大小的1/2。 Although if you end up going that route then I would strongly advise leaving a comment above the code explaining what it does and linking back to the original StackOverflow post. 虽然如果您最终选择了那条路线,那么我强烈建议您在代码上方留下一条注释,以解释它的作用,然后链接回原始的StackOverflow帖子。 The comment will get stripped by any good build tool so it won't affect final build size, but it will make life a lot easier for future developers. 任何好的构建工具都会删除此注释,因此它不会影响最终的构建大小,但它将使将来的开发人员的工作更加轻松。

Reducing Size Further 进一步缩小尺寸

If you really want a small answer, you can take advantage of arrow functions, destructuring, enhanced object literals, and the comma operator to shrink our previous reduce/map answer even further: 如果您真的想得到一个小的答案,则可以利用箭头功能,解构,增强的对象常量以及逗号运算符来进一步缩小我们先前的reduce / map答案:

var times = [["8:30-12:00","14:00-18:00"],["6:15-9:30","13:00-16:00","20:00-23:15"],[],["9:00-21:00"],["9:00-21:00"],[],[]];
var days = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];
var start, end;
var obj = times.reduce((acc, el, idx) => (
    acc[days[idx]] = el.map(timespan => (
        [start, end] = timespan.split("-"), {start, end}
    )), acc
), {});

Although good luck making heads or tails of that in the morning :) 虽然好运在早晨让它起了头或尾巴:)

Edit: Breaking down the above enhancements 编辑:分解以上增强

Since this answer actually received some attention I thought I'd expand it a bit to describe the four enhancements I used to shrink the code even further: 因为这个答案实际上引起了一些关注,所以我想我将它扩展一些以描述我用来进一步缩小代码的四个增强功能:

  • Arrow functions 箭头功能

Arrow functions are short-hand which can save you bytes in three ways (two of which we take advantage of here): 箭头函数是简写形式,可以通过三种方式为您节省字节(这里我们利用其中的两种方式):

var func = x => x * 2

// is functionally equivalent to:

var func = (function(x)
{
    return x * 2;
}).bind(this);

They remove the function keyword, they have an implicit return statement, and they bind the parent's scope (which is unimportant in this context). 它们删除了function关键字,具有隐式的return语句,并且绑定了父级的作用域(在此上下文中这并不重要)。 This means that the simple identity function function(x){return x;} can be reduced from 22 bytes to 4 ( x=>x ). 这意味着可以将简单的标识函数function(x){return x;}从22个字节减少到4个( x=>x )。 With a few exceptions this 18-byte savings is fairly consistent for the use of arrow functions ( function = 8 bytes, return = 6 bytes, and 4 more bytes are saved from not needing the parenthesis or brackets) 除少数例外,对于箭头功能的使用,这节省了18个字节( function = 8个字节, return = 6个字节,并且不需要括号或方括号又节省了4个字节)

  • Destructuring 解构

Destructuring is when you take a structured data type (array or object) and break it out into individual variables. 解构是指采用结构化数据类型(数组或对象)并将其分解为单个变量时。 For instances: 为实例:

var arr = [1, 2, 3];
var first = arr[0];
var second = arr[1];
var third = arr[2];

This has destructured the array into three variables: first , second , and third . 这已解体的阵列分成三个变量: firstsecond ,和third

Some languages (including modern javascript) have syntax to automatically destructure things for you: 某些语言(包括现代javascript)具有可以自动为您分解内容的语法:

var [first, second, third] = [1, 2, 3];

The space savings here should be fairly obvious. 这里的空间节省应该是相当明显的。

  • Enhanced object literals 增强的对象文字

Object literals refer to using squiggly brackets to create a javascript object. 对象字面量指的是使用方括号创建一个javascript对象。 For instance: {foo: 1, bar: 2} is an object literal. 例如: {foo: 1, bar: 2}是对象文字。 In modern javascript, object literals have been enhanced with certain short-hands. 在现代javascript中,对象文字已通过某些速记方式得到了增强 We take advantage of the following: 我们利用以下优势:

var a = 1;
var b = 2;
var obj = {
    a: a,
    b: b
};

// is functionally equivalent to:

var a = 1;
var b = 2;
var obj = {a, b};

Effectively if you provide a key but not a value, it assumes the value resides in a variable with the same name as the key. 实际上,如果您提供键但不提供值,则它将假定值位于与键同名的变量中。 So {start, end} is short-hand for {start: start, end: end} 因此{start, end}{start: start, end: end}简写

  • Comma operator 逗号运算符

This is a lesser-known feature of javascript that's often abused by code minifiers like UglifyJS. 这是鲜为人知的javascript功能,通常会被诸如UglifyJS之类的代码压缩工具滥用。 To understand it, it helps to understand the distinction between statements, expressions, and operators. 要理解它,它有助于理解语句,表达式和运算符之间的区别。

In programming, a statement is a single "thing" that you want to do. 在编程中, 语句是您要执行的单个“操作”。 Like var a = 3; var a = 3; -- you want to take the value 3 and put it into the variable a . -您想取值3并将其放入变量a Code is executed one statement at a time. 一次执行一条语句的代码。

An expression is a single computation. 表达式是单个计算。 Sometimes a statement consists of multiple expressions. 有时一条语句由多个表达式组成。 In the above example ( var a = 3; ) the numerical literal 3 is an expression. 在上面的示例中( var a = 3; ),数字文字3是一个表达式。 If we had instead written var a = 3 * 2 * 1; 如果我们改为写var a = 3 * 2 * 1; then 3 * 2 * 1 is an expression which itself can be decomposed into the expressions 3 * 2 and x * 1 (where x is the output of the previous expression) 那么3 * 2 * 1是一个表达式,它本身可以分解为表达式3 * 2x * 1 (其中x是前一个表达式的输出)

An operator is a symbol or keyword that tells the processor to do a thing -- like add, subtract, or read from memory. 运算符是一个符号或关键字,它告诉处理器执行某件事-例如加,减或从内存中读取。 For instance, * is an operator that says to multiply. 例如, *是一个表示相乘的运算符。 Some operators are known as unary operators because they take one parameter. 一些运算符被称为一元运算符,因为它们采用一个参数。 For instance, the increment operator ( ++ ) takes a single variable and adds one to it: a++; 例如,增量运算符( ++ )接受一个变量并将其添加到其中: a++; . Some operators are known as binary operators because they take two parameters. 一些运算符被称为二进制运算符,因为它们采用两个参数。 For instance, the addition operator ( + ) takes two variables and adds them together: a+b; 例如,加法运算符( + )接受两个变量并将它们加在一起: a+b; . To my knowledge there's only one ternary operator which takes three parameters: The if-else operator ( ?: ) which looks like so: a ? b : c 据我所知,只有一个三元运算符a ? b : c三个参数:if-else运算符( ?: :)看起来像这样: a ? b : c a ? b : c -- this is kind of like if (a) { b } else { c } a ? b : c这有点像if (a) { b } else { c }

So now understanding the difference between statements, expressions, and operators, we can say that the comma is an operator that processes expressions from left-to-right, then returns the right-most value. 因此,现在了解了语句,表达式和运算符之间的区别之后,我们可以说逗号是一个运算符 ,它从左到右处理表达式,然后返回最右边的值。 So the expressions a,b,c will always evaluate to c . 因此,表达式a,b,c将始终取值为c Meanwhile the expression var d = (a=1,b=a+1,c=b+1); 同时表达式var d = (a=1,b=a+1,c=b+1); will evaluate like so: 将会像这样评估:

  1. Set a equal to 1 设置a等于1
  2. Read a (1), add 1 to it, and store the result in b 读取a (1),将其加1,然后将结果存储在b
  3. Read b (2), add 1 to it, and store the result in c b (2),加1,然后将结果存储在c
  4. The = operator, after storing a value in memory, returns the value that was stored (this is why a=b=c=1 will properly set all three variable). 在将值存储在内存中后, =运算符将返回已存储的值(这就是为什么a=b=c=1会正确设置所有三个变量的原因)。 So the = operator now returns 3 所以=运算符现在返回3
  5. The , operator on the right will return the result of the = expression: 3 右侧的,运算符将返回=表达式的结果: 3
  6. The , operator on the left will return the result of the , operator on the right: 3 左侧的,运算符将返回右侧的,运算符的结果: 3
  7. 3 is stored into d 3存入d

We take advantage of the comma operator to extend our arrow functions. 我们利用逗号运算符来扩展箭头功能。 Normally in order for an arrow function to implicitly return a value, the arrow function must contain only a single statement: 通常,为了使箭头函数隐式返回值,箭头函数必须仅包含一个语句:

// Implicit return:
x => x * 2;

// No implicit return:
x => {
    y += x;
    return x * 2;
}

By using the comma operator we can keep the implicit return while still executing multiple statements: 通过使用逗号运算符,我们可以在执行多个语句的同时保留隐式返回:

x => (
    y += x,
    x * 2
)

We do this in the reducer to simultaneously set a value on our accumulator ( acc[day] = ... ) and return the accumulator ( , acc ) 我们在减速器中执行此操作,以同时在累加器上设置一个值( acc[day] = ... )并返回累加器( , acc

Putting all of these tricks together we took: 综合所有这些技巧,我们采取了以下措施:

var obj = times.reduce(
    function(acc, el, idx)
    {
        acc[days[idx]] = el.map(
            function(timespan)
            {
                var splitStr = timespan.split("-");
                return {
                    start: ("0" + splitStr[0]).substr(-5),
                    end: ("0" + splitStr[1]).substr(-5)
                };
            }
        );
        return acc;
    },
    {}
);

And changed it to the following: 并将其更改为以下内容:

var obj = times.reduce(
    // Arrow function
    (acc, el, idx) =>
    // Instead of {}, we use () with the comma operator
    (
        acc[days[idx]] = el.map(
            // Arrow function
            timespan =>
            // Instead of {}, we use () with the comma operator
            (
                // Destructuring
                var [start, end] = timespan.split("-");
                // Comma operator, return "{start, end}"
                ,
                // Enhanced object literal
                {start, end}
            )
        )
        // Comma operator, returns "acc"
        ,
        acc
    ),
    {}
);

When I tested this code it threw a syntax error, effectively saying that you can't use var within the comma operator. 当我测试此代码时,它引发了语法错误,有效地表明您不能在逗号运算符内使用var I wasn't aware of this, but I corrected it by defining start and end up above: 我没有意识到这一点,但是我通过在上面定义startend来更正了它:

var start, end;
var obj = times.reduce(
    // ... Lots of stuff
                [start, end] = el.split("-");
    // ... Lot sof stuff
);

After that I just removed comments and whitespace to get: 之后,我删除了注释和空格以获取:

var start, end;
var obj = times.reduce((acc, el, idx) => (
    acc[days[idx]] = el.map(timespan => (
        [start, end] = timespan.split("-"), {start, end}
    )), acc
), {});

Potential for Further Reduction 进一步减少的潜力

I toyed around with a few ideas that may be able to shrink the code even smaller , but couldn't quite make anything work. 我开玩笑地提出了一些想法,这些想法可能能够将代码缩小到甚至更小 ,但无法完全起作用。

Using the object spread operator you can avoid the need to call the comma operator on reduce like so: 使用对象传播运算符,您可以避免像这样在reduce上调用逗号运算符:

var obj = times.reduce((acc, el, idx) => ({
    ...acc, [days[idx]]: el
}), {});

// is functionally equivalent to:

var obj = times.reduce((acc, el, idx) => {
    acc[days[idx]] = el;
    return acc;
}, {});

// which we used the comma operator to reduce to:

var obj = times.reduce((acc, el, idx) => (
    acc[days[idx]] = el, acc
), {});

Once white space is removed the object spread version is 6 bytes smaller than the standard solution (with return acc; ). 除去空白后,对象展开版本比标准解决方案(带有return acc; )小6个字节。 Meanwhile our comma operator solution is actually the smallest with 2 additional bytes removed. 同时,我们的逗号运算符解决方案实际上是最小的,删除了2个额外的字节。 Although keeping the object spread operator in our back pocket may be helpful later. 虽然将对象散布算子放在我们的后兜可能会有所帮助。

By using Map we can eliminate the call to reduce and also eliminate the default value of the accumulator like so: 通过使用Map我们可以消除reduce的调用,也可以消除累加器的默认值,如下所示:

var map = new Map(times.map((el, idx) => [days[idx], el]));

// is *kind of the same* (but not identical) as:

var obj = times.reduce((acc, el, idx) => (
    acc[days[idx]] = el, acc
), {});

This saves us 10 bytes on our comma operator solution, but comes at the cost of returning a Map instead of an Object . 这为我们在逗号运算符解决方案上节省了10个字节,但以返回Map而不是Object为代价。 To convert a Map back to an Object currently requires the use of the reduce method - but there have been a few proposals for a fromEntries method or similar ( UPDATE: Object.fromEntries has been finalized and included in many browsers, but it didn't end up saving space. See below) 要将Map转换回Object目前需要使用reduce方法-但是有一些关于fromEntries方法或类似方法的建议( UPDATE: Object.fromEntries已经完成并包含在许多浏览器中,但并未最终节省了空间。请参见下文)

In general if you can create an array of the form: 通常,如果您可以创建以下形式的数组:

[[key, value], [key, value], ...]

Then this matches the output of: 然后,这与以下内容的输出匹配:

var obj = {...};
obj.entries();

It's useful for initializing Map s, and may eventually be useful for creating Object s. 这对于初始化Map很有用,最终可能对创建Object有用。

Using Object.assign we can convert an array into an object very quickly, but the keys will be wrong: 使用Object.assign我们可以非常快速地将数组转换为对象,但是键将是错误的:

var obj = Object.assign({}, [1, 2, 3]);
console.log(obj);
// {0: 1, 1: 2, 2: 3}

It may be possible to take advantage of this if we could somehow adjust the keys after the fact 如果我们可以在事发后以某种方式调整按键,则可以利用此功能

So at the moment the answer I gave earlier is the best I've got, but additional byte savings may be possible if you're willing to make your code even less human readable 所以此刻我在前面给出的答案是我已经得到了最好的,但如果你愿意让你的代码就更少了人类可读的额外字节的储蓄是可能

Object.fromEntries

With Object.fromEntries now available in many browsers, I remembered this post and wanted to see if it could be improved. 现在在许多浏览器中都可以使用Object.fromEntries ,我记得这篇文章,想看看是否可以改进。 So I gave it a try: 因此,我尝试了一下:

var times = [["8:30-12:00","14:00-18:00"],["6:15-9:30","13:00-16:00","20:00-23:15"],[],["9:00-21:00"],["9:00-21:00"],[],[]];
var days = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];
var start, end;
var obj = Object.fromEntries(new Map(times.map((el, idx) => 
    [
        days[idx],
        el.map(timespan => (
            [start, end] = timespan.split("-"), {start, end}
        ))
    ]
)));

This replaces: 它代替:

  • times.reduce((acc,el,idx) with new Map(times.map((el,idx) (4 additional bytes after variable minification, actually) new Map(times.map((el,idx) times.reduce((acc,el,idx) new Map(times.map((el,idx)实际上是变量new Map(times.map((el,idx)后还有4个字节)
  • acc[days[idx]]= with [days[idx],] (1 byte saved after variable minification) acc[days[idx]]=[days[idx],] (变量最小化后保存1个字节)
  • ,acc with nothing (2 bytes saved after minification) ,acc 一无所有 (缩小后保存2个字节)
  • ,{} with nothing (3 bytes saved) ,{} 一无所有 (节省了3个字节)

But required I add the additional Object.fromEntries() (20 bytes, can't be minified). 但是需要添加额外的Object.fromEntries() (20个字节,不能缩小)。 That's a net gain of 22 bytes. 这是22个字节的净收益 Still, it was fun to try. 不过,尝试还是很有趣的。

Removing the days variable 删除days变量

While we do want an array to map indexes to day names, we don't have to store this into a variable. 虽然我们确实希望数组将索引映射到日期名称,但是我们不必将其存储到变量中。 We can therefore save a teensy bit of space like so: 因此,我们可以节省的,像这样的空间有点:

var times = [["8:30-12:00","14:00-18:00"],["6:15-9:30","13:00-16:00","20:00-23:15"],[],["9:00-21:00"],["9:00-21:00"],[],[]];
var start, end;
var obj = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"].reduce((acc, el, idx) => (
    acc[el] = times[idx].map(timespan => (
        [start, end] = timespan.split("-"), {start, end}
    )), acc
), {});

This removes var days= (6 bytes saved after minification), replaces days[idx] with el (3 bytes saved), and replaces el with times[idx] (3 bytes gained) for a net 6 bytes savings. 这将删除var days= (最小化后节省6个字节),用el (替换为3个字节)替换days[idx] ,并用times[idx] (获取3个字节)替换el以节省6个字节。

Note on minfication 注意事项

I plugged the three answers (my original, with Object.fromEntries , and with the days array removed) into UglifyJS to verify my theory on variable minification (every variable name will be reduced to 1 byte, other symbols will remain as-is) and UglifyJS seems to have found a few other optimizations along the way. 我将三个答案(我的原始答案,带有Object.fromEntries ,并且删除了days数组)插入到UglifyJS中,以验证我有关变量最小化的理论(每个变量名将减少为1个字节,其他符号将保持原样)和UglifyJS似乎在此过程中发现了其他一些优化。

  • Original answer: 314 bytes 原始答案:314个字节
  • Object.fromEntries 330 bytes (only 16 more, not 22) Object.fromEntries 330字节(仅增加16个字节,而不是22个字节)
  • Removing days array: 304 bytes (saved 10, not 6) 删除days数组:304个字节(保存10个字节,而不是6个字节)

It's worth noting that UglifyJS detected start and end as global variables (since I copied the code directly without encapsulating it in a function) so they were not minified, and it seems to have replaced my enhanced object literal with a simpler object literal ( {start,end} was converted to {start:start,end:end} 值得注意的是,UglifyJS将startend检测为全局变量(因为我直接复制了代码而未将其封装在函数中),因此它们没有被缩小,并且似乎已经用一个更简单的对象常量( {start,end}已转换为{start:start,end:end}

var start,end,times=[["8:30-12:00","14:00-18:00"],["6:15-9:30","13:00-16:00","20:00-23:15"],[],["9:00-21:00"],["9:00-21:00"],[],[]],obj=["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].reduce((a,t,d)=>(a[t]=times[d].map(a=>([start,end]=a.split("-"),{start:start,end:end})),a),{});

With minification my "recommended" answer (human readable) came out to 475 bytes. 经过最小化,我的“推荐”答案(可读)达到了475个字节。 It's 56% larger, but I think the clarity in your source code is worthwhile. 它大了56%,但是我认为您的源代码中的清晰度值得。

for(var times=[["8:30-12:00","14:00-18:00"],["6:15-9:30","13:00-16:00","20:00-23:15"],[],["9:00-21:00"],["9:00-21:00"],[],[]],days=["sunday","monday","tuesday","wednesday","thursday","friday","saturday"],timeObj={},i=0;i<times.length;i++){timeObj[days[i]]=[];for(var j=0;j<times[i].length;j++){var timespan=times[i][j],timespanArr=timespan.split("-"),timespanObj={start:("0"+timespanArr[0]).substr(-5),end:("0"+timespanArr[1]).substr(-5)};timeObj[days[i]].push(timespanObj)}}

One Final Thought 最后的想法

I touched briefly on the possibility of bad data in your strings. 我简短地谈到了字符串中错误数据的可能性。 For instance, what would you do if someone put in: 8:30+12:00 instead of 8:30-12:00 ? 例如,如果有人输入: 8:30+12:00而不是8:30-12:00 ,您会怎么做? In the case of my code: 就我的代码而言:

  • The split would fail (not finding a hyphen) so we'd produce an array of one element: ["8:30+12:00"] split将失败(找不到连字符),因此我们将生成一个元素数组: ["8:30+12:00"]
  • splitStr[0] would be "8:30+12:00" splitStr[0]将为"8:30+12:00" splitStr[0] "8:30+12:00"
  • "0" + splitStr[0] would be "08:30+12:00" "0" + splitStr[0]"08:30+12:00"
  • ("08:30+12:00").substr(-5) would be "12:00" ("08:30+12:00").substr(-5)"12:00"
  • splitStr[1] would be undefined splitStr[1]将不确定
  • "0" + splitStr[1] would just be "0" "0" + splitStr[1]仅为"0"
  • ("0").sustr(-5) would again just be "0" ("0").sustr(-5)再次为"0"
  • The resulting timespan would be: {start: "12:00", end: "0"} 结果时间跨度为: {start: "12:00", end: "0"}

Notice that it didn't actually throw an error, no exceptions were raised, and the code didn't complain. 注意,它实际上并没有引发错误,没有引发异常,并且代码没有抱怨。 Now you'll have all sorts of bugs in the future and have trouble tracking them down, because this code silently failed . 现在,将来您将遇到各种各样的错误,并且很难找到它们,因为此代码默默地失败了 Silent failure is the worst. 无声的失败是最糟糕的。

The general solution to reduce such silent failures is to minimize the number of possible values that all data can take. 减少此类静默故障的一般解决方案是将所有数据可以采用的可能值的数量最小化。 An integer can't take decimal values, so if you don't need decimal values then you use an integer instead of a float when writing code in C++ or Java. 整数不能采用十进制值,因此,如果不需要十进制值,则在用C ++或Java编写代码时,可以使用整数而不是浮点数。 By allowing decimals you create a possible failure state. 通过允许小数,您可以创建可能的故障状态。 JavaScript doesn't have "integers" and "floats" (it just has "Number"), but it does still have types: JavaScript没有“整数”和“浮动”(它只是有“数”),但仍然有两类:

  • Number
  • String
  • Object 宾语
  • Array 数组
  • Date 日期
  • etc 等等

By using the right type, you avoid a lot of errors. 通过使用正确的类型,可以避免很多错误。 In your code, you're using an anti-pattern that's commonly known as Stringly Typed Variables . 在您的代码中,您使用的是反模式,通常称为“ 字符串类型变量” That is - you're using strings to represent something that probably shouldn't be a string. 那就是-您正在使用字符串来表示可能不应该是字符串的东西。

Instead of "08:30" , consider using either a Number (to represents milliseconds or seconds since midnight) or a Date 代替使用"08:30" ,考虑使用数字(代表午夜以来的毫秒或秒)或日期

As for which to use, that's up to you. 至于使用哪种,则取决于您。 Using a Date provides many conveniences like being able to add and subtract times efficiently ( 11:30 + 2:00 = 1:30 PM ) but it comes with the added complexity that JavaScript dates imply both a time and day. 使用日期可提供许多便利,例如能够有效地增加和减少时间( 11:30 + 2:00 = 1:30 PM ),但它带来的额外复杂性是JavaScript日期意味着时间日期。 Since you don't care about the day you'll likely have to do something like "set the day to 1970-01-01 (epoch)". 由于您不关心日期,因此可能需要执行诸如“将日期设置为1970-01-01(纪元)”之类的操作。 Furthermore, the JavaScript Date object brings with it a timezone -- something else you'll have to work around. 此外,JavaScript Date对象带有一个时区-您还需要解决其他问题。

So long as you're parsing and converting the object, anyway, you might as well get it into a more reasonable type to prevent future string-related problems. 无论如何,只要解析和转换对象,就最好将其转换为更合理的类型,以防止将来出现与字符串有关的问题。

Here you go: 干得好:

 let dayNames = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'] let times = [ ["8:30-12:00", "14:00-18:00"], ["6:15-9:30", "13:00-16:00", "20:00-23:15"], [], ["9:00-21:00"], ["9:00-21:00"], [], [] ] const res = {} for (let i = 0; i < 7; i++) { const items = [] res[dayNames[i]] = items times[i].forEach(pair => { const parts = pair.split('-') items.push({ start: parts[0], end: parts[1] }) }) } console.log(res); 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM