什么时候用数组,什么时候用对象?
When to use array and when to use object?
我最近读了很多关于对象和数组的文章,但出于某种原因它们有相反的信息/事实。我需要你的帮助来一劳永逸地学习:什么时候最好使用数组,什么时候使用对象?
显而易见的原因是当您需要特定顺序时使用数组。还有什么?这个例子怎么样?
- 有很多推动
- 有很多检查数组是否包含东西
- 有很多删除
请注意,此示例中我需要的数字要小得多(以千为单位)。
我为这个问题写的代码,它有一些我永远不会做的事情,但是为了这个问题:
var x = [];
var y = [];
var z = [];
var clickedX = [];
var clickedY = [];
var clickedZ = [];
var count = 100;
for ( var a = 0; a < count; a++ ) {
//For the sake of this example: random int 1, 2 or 3
var type = Math.floor(Math.random() * 3) + 1;
createDiv( type );
}
function createDiv( thisType ) {
var self = this;
var div = self.div;
var type = thisType;
if( !div ) {
div = this.div = document.createElement( 'div' );
div.className = 'marker';
//Push to "right" array
if( type == '1' ) {
x.push( self );
}
else if ( type == '2' ) {
y.push( self );
}
else {
z.push( self );
}
//Attach click event
div.onclick = function() {
// X
var indexX = x.indexOf( self );
if( indexX > -1 ) {
//Push to new array
clickedX.push( self );
//Remove from old array
x.splice( indexX, 1 );
}
// Y
var indexY = y.indexOf( self );
if( indexY > -1 ) {
//Push to new array
clickedY.push( self );
//Remove from old array
y.splice( indexY, 1 );
}
// Z
var indexZ = z.indexOf( self );
if( indexZ > -1 ) {
//Push to new array
clickedZ.push( self );
//Remove from old array
z.splice( indexZ, 1 );
}
}; // onclick
} // if( !div )
} // createDiv()
我目前正在处理的数据示例:
// Data Im dealing with
objects = {
{ objects1: [
0: { object : [4-5 assigned values (int, string, array)] },
1: { object : [4-5 assigned values (int, string, array)] },
//etc
]
},
{ objects2: [
0: {object},
1: {object},
//etc
]
}
}
// 1. I need to iterate through every "object" in both "objects1" and "objects2"
// 2. I need to iterate through "array" in every "object" in "objects1"
for( /* "object" count in "objects1" */ ) {
//Do something for each "object" in "objects1"
for( /* items in "array" count */ ) {
//Do something for each item in "array"
}
}
// AND
for( /* "object" count in "objects2" */ ) {
//Do something for each "object" in "objects2"
}
数组
基本上,当您有一个结果数据列表并且要将其作为一个集合使用时,您需要使用一个数组。
它很容易添加、迭代、获取计数和检查为了存在.
很难删除 数组中的项目。
此外,它存储订单。
比如你有10个整数,你要找最小的一个的索引,那么用数组就顺理成章了。
对象
使用对象,当你的项目有键(特别是,non-integer)并且你要一个一个地使用它们时。
方便添加、删除、检查是否存在属性。
但是,通过对象属性迭代不太方便,并且获取它的计数是绝对不合适的。
也无法存储物品的订单。
时间复杂度
回答你关于推送、检查存在和删除的问题:
- 属性 设置和数组推送都需要 O(1) 时间,但我坚信第一个慢一点
- 检查是否存在需要相同的 O(1) 时间
- 删除一个元素不是数组设计的目的 - 它需要移动所有项目并花费 O(n) 时间,而您可以为 O(1)
删除一个 属性
错误的用法示例
有一些非常糟糕的用法。如果你有以下情况,那么你用错了数组或对象。
给数组分配一个随机值:
var a = [];
a[1000] = 1;
它将数组的 length
属性 更改为 1001,如果您尝试输出此数组,您将得到以下内容:
[undefined,undefined,undefined,undefined... 1]
如果不是故意的,那绝对没用。
为对象分配结果值
var a = {};
for (var i = 0; i < 10; i++)
{
a[i] = 1;
}
您的代码
谈到您的代码,有很多方法可以正确地做到这一点 - 只需选择一个。我会按以下方式进行:
var items = [];
for (var i = 0; i < count; i++)
{
var o = {
type: Math.floor(Math.random() * 3) + 1,
isClicked: false
};
var div = document.createElement('div');
div.className = 'marker';
div.onclick = function() {
o.isClicked = true;
};
o.div = div;
items.push(o);
}
function GetDivs(type, isClicked)
{
var result = items;
if (type)
result = result.filter(function(x) { return x.type === type; });
if (isClicked !== undefined)
result = result.filter(function(x) { return x.isClicked === isClicked; });
return result;
}
然后,您将能够得到任何您想要的物品:
var all = GetItems(0); // or even GetDivs()
var allClicked = GetItems(0, true);
var allNotClicked = GetItems(0, false);
var divsTypeA = GetItems(1);
var clickedDivsTypeB = GetItems(2, true);
var notClickedDivsTypeC = GetItems(3, false);
使用这种用法,您根本不需要删除任何项目 - 您只需将它们标记为已点击或未点击,这需要 O(1) 时间。
jQuery
如果您使用 jQuery 和数据 HTML 属性,那么您根本不需要使用数组或对象:
for (var i = 0; i < count; i++)
{
$('<div/>')
.addClass('marker')
.attr('data-type', Math.floor(Math.random() * 3) + 1)
.appendTo('body')
.click(function() {
$(this).addClass('clicked');
});
}
现在,您可以使用选择器查找任何项目:
var all = $('.marker');
var allClicked = $('.marker.clicked');
var allNotClicked = $('.marker:not(.clicked)');
var divsTypeA = $('.marker[data-type='1']');
var clickedDivsTypeB = $('.marker[data-type='2'].clicked');
var notClickedDivsTypeC = $('.marker[data-type='1']:not(.clicked)');
但是,如果您真的有数千条记录,最后一种方法可能会产生滞后。同时,在页面上放置 1000 个动态 div 是否是个好主意? :)
当所有其他方法都失败时 运行 测试,再次取决于您打算将数据结构用于什么,一切都是权衡,性能 ||效率。
准备等待几秒钟让测试完成。
function gimmeAHugeArray( length ){
var arr = [];
for( var ii = 0; ii < length; ii++ ){
arr.push( (ii * 100000 ).toString(16) );
}
return arr;
}
function gimmeAHugeObject( length ){
var obj = {};
for( var ii = 0; ii < length; ii++ ){
obj[ (ii * 100000 ).toString(16) ] = ii;
}
return obj;
}
var hugeArray = gimmeAHugeArray( 100000 );
var hugeObject = gimmeAHugeObject( 100000 );
var key = (8000 * 100000).toString(16);
console.perf(function arrayGetWithIndexOf(){
var val = hugeArray.indexOf( key );
});
console.perf(function objectGetWithKey(){
var val = hugeObject[ key ];
});
console.perf(function arrayIterateAllProperties(){
var val = null;
for( var ii = 0, ll = hugeArray.length; ii < ll; ii++ ){
val = hugeArray[ii];
}
}, 50);
console.perf(function objectIterateAllProperties(){
var val = null,
key;
for( key in hugeObject ){
val = hugeObject[ key ];
}
}, 50);
<script src="http://codepen.io/synthet1c/pen/WrQapG.js"></script>
我将从 Solo 上面的评论中回答你的问题。
问题是如果我需要对相同的数据执行这两项操作怎么办?
我发现最好的方法(至少对我而言)是创建一个数组,它可能有空节点,但保留另一个数组来跟踪填充的 ID。
看起来像这样:
var MyArray = function()
{
this.storage = [];
this.ids = [];
};
MyArray.prototype.set = function(id, value)
{
this.storage[id] = value;
this.ids[this.ids.length] = id;
};
MyArray.prototype.get = function(id)
{
return this.storage[id];
};
MyArray.prototype.delete = function(id)
{
delete this.storage[id];
};
此解决方案适用于这两种方法,因为您可以在 O(1) 中删除任何内容,因为每个人都有给定的 ID,并且您可以轻松地对其进行迭代,因为您拥有所有 ID,这些值存储在.
我还创建了一个非常小的 class 供我自己使用,它的性能非常好。这是 link https://github.com/ImJustAskingDude/JavascriptFastArray . Here's a question I asked about these things: Javascript Array Performance .
请阅读我在那里写的内容,此解决方案适用于非常特殊的情况,我不知道它是否适用于您。
这就是为什么我要你阅读我提供的那些 link 中的内容,我已经写了大约 10 页的 material,尽管其中一些是纯粹的垃圾什么都不说。不管怎样,我也在这里写几个例子吧。
示例:
假设您有来自数据库的数据,来自数据库的数据非常需要具有唯一的整数 ID,但每个用户可能将它们分布在很宽的范围内,它们很可能不连续。
所以你从数据库中获取数据到对象中,就像这样:
var DBData /* well, probably something more descriptive than this stupid name */ = function(id, field1, field2, field3)
{
this.id = id;
this.field1 = field1;
this.field2 = field2;
this.fiedl3 = field3;
};
鉴于此 class,您将数据库中的所有数据写入它的实例,并将其存储在 MyArray 中。
var data = new MyArray();
// YOU CAN USE THIS, OR JUST READ FROM DOM
$.get(/* get data from DB into MyArray instance */, function(data) { /* parse it somehow */ var dbdata = new DBData(/* fields that you parsed including ID */); data.set(ID, dbdata)});
当你想遍历所有数据时,你可以这样做:
只需创建一个函数来创建一个新数组,将所有数据都放在一个漂亮的连续块中,它会像这样:
function hydrate(myarray, ids)
{
var result = [];
var length = ids.length;
if(!length)
{
return null;
}
var storage = myarray.storage;
for(var i = 0; i < length; i++)
{
result[result.length] = storage[ids[i]];
}
return result;
};
我使用这个方法,因为你可以用它select非常容易地将任何对象配置成一个连续的数组。
处理数组数组的一种解决方案是修改 MyArray 以专门处理它,或者制作一个版本来处理它。
一个不同的解决方案是重新考虑您的方法,也许将所有数据添加到一个 MyArray 中,但将所有
objects1: [
0: { object : [4-5 assigned values (int, string, array)] },
1: { object : [4-5 assigned values (int, string, array)] },
//etc
]
},
{ objects2: [
0: {object},
1: {object},
//etc
]
}
变成漂亮的对象。
说真的,请阅读我在那些 link 中写的内容,那里有我对所有内容的推理。
我最近读了很多关于对象和数组的文章,但出于某种原因它们有相反的信息/事实。我需要你的帮助来一劳永逸地学习:什么时候最好使用数组,什么时候使用对象?
显而易见的原因是当您需要特定顺序时使用数组。还有什么?这个例子怎么样?
- 有很多推动
- 有很多检查数组是否包含东西
- 有很多删除
请注意,此示例中我需要的数字要小得多(以千为单位)。
我为这个问题写的代码,它有一些我永远不会做的事情,但是为了这个问题:
var x = [];
var y = [];
var z = [];
var clickedX = [];
var clickedY = [];
var clickedZ = [];
var count = 100;
for ( var a = 0; a < count; a++ ) {
//For the sake of this example: random int 1, 2 or 3
var type = Math.floor(Math.random() * 3) + 1;
createDiv( type );
}
function createDiv( thisType ) {
var self = this;
var div = self.div;
var type = thisType;
if( !div ) {
div = this.div = document.createElement( 'div' );
div.className = 'marker';
//Push to "right" array
if( type == '1' ) {
x.push( self );
}
else if ( type == '2' ) {
y.push( self );
}
else {
z.push( self );
}
//Attach click event
div.onclick = function() {
// X
var indexX = x.indexOf( self );
if( indexX > -1 ) {
//Push to new array
clickedX.push( self );
//Remove from old array
x.splice( indexX, 1 );
}
// Y
var indexY = y.indexOf( self );
if( indexY > -1 ) {
//Push to new array
clickedY.push( self );
//Remove from old array
y.splice( indexY, 1 );
}
// Z
var indexZ = z.indexOf( self );
if( indexZ > -1 ) {
//Push to new array
clickedZ.push( self );
//Remove from old array
z.splice( indexZ, 1 );
}
}; // onclick
} // if( !div )
} // createDiv()
我目前正在处理的数据示例:
// Data Im dealing with
objects = {
{ objects1: [
0: { object : [4-5 assigned values (int, string, array)] },
1: { object : [4-5 assigned values (int, string, array)] },
//etc
]
},
{ objects2: [
0: {object},
1: {object},
//etc
]
}
}
// 1. I need to iterate through every "object" in both "objects1" and "objects2"
// 2. I need to iterate through "array" in every "object" in "objects1"
for( /* "object" count in "objects1" */ ) {
//Do something for each "object" in "objects1"
for( /* items in "array" count */ ) {
//Do something for each item in "array"
}
}
// AND
for( /* "object" count in "objects2" */ ) {
//Do something for each "object" in "objects2"
}
数组
基本上,当您有一个结果数据列表并且要将其作为一个集合使用时,您需要使用一个数组。
它很容易添加、迭代、获取计数和检查为了存在.
很难删除 数组中的项目。
此外,它存储订单。
比如你有10个整数,你要找最小的一个的索引,那么用数组就顺理成章了。
对象
使用对象,当你的项目有键(特别是,non-integer)并且你要一个一个地使用它们时。
方便添加、删除、检查是否存在属性。 但是,通过对象属性迭代不太方便,并且获取它的计数是绝对不合适的。
也无法存储物品的订单。
时间复杂度
回答你关于推送、检查存在和删除的问题:
- 属性 设置和数组推送都需要 O(1) 时间,但我坚信第一个慢一点
- 检查是否存在需要相同的 O(1) 时间
- 删除一个元素不是数组设计的目的 - 它需要移动所有项目并花费 O(n) 时间,而您可以为 O(1) 删除一个 属性
错误的用法示例
有一些非常糟糕的用法。如果你有以下情况,那么你用错了数组或对象。
给数组分配一个随机值:
var a = []; a[1000] = 1;
它将数组的
length
属性 更改为 1001,如果您尝试输出此数组,您将得到以下内容:[undefined,undefined,undefined,undefined... 1]
如果不是故意的,那绝对没用。
为对象分配结果值
var a = {}; for (var i = 0; i < 10; i++) { a[i] = 1; }
您的代码
谈到您的代码,有很多方法可以正确地做到这一点 - 只需选择一个。我会按以下方式进行:
var items = [];
for (var i = 0; i < count; i++)
{
var o = {
type: Math.floor(Math.random() * 3) + 1,
isClicked: false
};
var div = document.createElement('div');
div.className = 'marker';
div.onclick = function() {
o.isClicked = true;
};
o.div = div;
items.push(o);
}
function GetDivs(type, isClicked)
{
var result = items;
if (type)
result = result.filter(function(x) { return x.type === type; });
if (isClicked !== undefined)
result = result.filter(function(x) { return x.isClicked === isClicked; });
return result;
}
然后,您将能够得到任何您想要的物品:
var all = GetItems(0); // or even GetDivs()
var allClicked = GetItems(0, true);
var allNotClicked = GetItems(0, false);
var divsTypeA = GetItems(1);
var clickedDivsTypeB = GetItems(2, true);
var notClickedDivsTypeC = GetItems(3, false);
使用这种用法,您根本不需要删除任何项目 - 您只需将它们标记为已点击或未点击,这需要 O(1) 时间。
jQuery
如果您使用 jQuery 和数据 HTML 属性,那么您根本不需要使用数组或对象:
for (var i = 0; i < count; i++)
{
$('<div/>')
.addClass('marker')
.attr('data-type', Math.floor(Math.random() * 3) + 1)
.appendTo('body')
.click(function() {
$(this).addClass('clicked');
});
}
现在,您可以使用选择器查找任何项目:
var all = $('.marker');
var allClicked = $('.marker.clicked');
var allNotClicked = $('.marker:not(.clicked)');
var divsTypeA = $('.marker[data-type='1']');
var clickedDivsTypeB = $('.marker[data-type='2'].clicked');
var notClickedDivsTypeC = $('.marker[data-type='1']:not(.clicked)');
但是,如果您真的有数千条记录,最后一种方法可能会产生滞后。同时,在页面上放置 1000 个动态 div 是否是个好主意? :)
当所有其他方法都失败时 运行 测试,再次取决于您打算将数据结构用于什么,一切都是权衡,性能 ||效率。
准备等待几秒钟让测试完成。
function gimmeAHugeArray( length ){
var arr = [];
for( var ii = 0; ii < length; ii++ ){
arr.push( (ii * 100000 ).toString(16) );
}
return arr;
}
function gimmeAHugeObject( length ){
var obj = {};
for( var ii = 0; ii < length; ii++ ){
obj[ (ii * 100000 ).toString(16) ] = ii;
}
return obj;
}
var hugeArray = gimmeAHugeArray( 100000 );
var hugeObject = gimmeAHugeObject( 100000 );
var key = (8000 * 100000).toString(16);
console.perf(function arrayGetWithIndexOf(){
var val = hugeArray.indexOf( key );
});
console.perf(function objectGetWithKey(){
var val = hugeObject[ key ];
});
console.perf(function arrayIterateAllProperties(){
var val = null;
for( var ii = 0, ll = hugeArray.length; ii < ll; ii++ ){
val = hugeArray[ii];
}
}, 50);
console.perf(function objectIterateAllProperties(){
var val = null,
key;
for( key in hugeObject ){
val = hugeObject[ key ];
}
}, 50);
<script src="http://codepen.io/synthet1c/pen/WrQapG.js"></script>
我将从 Solo 上面的评论中回答你的问题。
问题是如果我需要对相同的数据执行这两项操作怎么办?
我发现最好的方法(至少对我而言)是创建一个数组,它可能有空节点,但保留另一个数组来跟踪填充的 ID。
看起来像这样:
var MyArray = function()
{
this.storage = [];
this.ids = [];
};
MyArray.prototype.set = function(id, value)
{
this.storage[id] = value;
this.ids[this.ids.length] = id;
};
MyArray.prototype.get = function(id)
{
return this.storage[id];
};
MyArray.prototype.delete = function(id)
{
delete this.storage[id];
};
此解决方案适用于这两种方法,因为您可以在 O(1) 中删除任何内容,因为每个人都有给定的 ID,并且您可以轻松地对其进行迭代,因为您拥有所有 ID,这些值存储在.
我还创建了一个非常小的 class 供我自己使用,它的性能非常好。这是 link https://github.com/ImJustAskingDude/JavascriptFastArray . Here's a question I asked about these things: Javascript Array Performance .
请阅读我在那里写的内容,此解决方案适用于非常特殊的情况,我不知道它是否适用于您。
这就是为什么我要你阅读我提供的那些 link 中的内容,我已经写了大约 10 页的 material,尽管其中一些是纯粹的垃圾什么都不说。不管怎样,我也在这里写几个例子吧。
示例:
假设您有来自数据库的数据,来自数据库的数据非常需要具有唯一的整数 ID,但每个用户可能将它们分布在很宽的范围内,它们很可能不连续。
所以你从数据库中获取数据到对象中,就像这样:
var DBData /* well, probably something more descriptive than this stupid name */ = function(id, field1, field2, field3)
{
this.id = id;
this.field1 = field1;
this.field2 = field2;
this.fiedl3 = field3;
};
鉴于此 class,您将数据库中的所有数据写入它的实例,并将其存储在 MyArray 中。
var data = new MyArray();
// YOU CAN USE THIS, OR JUST READ FROM DOM
$.get(/* get data from DB into MyArray instance */, function(data) { /* parse it somehow */ var dbdata = new DBData(/* fields that you parsed including ID */); data.set(ID, dbdata)});
当你想遍历所有数据时,你可以这样做:
只需创建一个函数来创建一个新数组,将所有数据都放在一个漂亮的连续块中,它会像这样:
function hydrate(myarray, ids)
{
var result = [];
var length = ids.length;
if(!length)
{
return null;
}
var storage = myarray.storage;
for(var i = 0; i < length; i++)
{
result[result.length] = storage[ids[i]];
}
return result;
};
我使用这个方法,因为你可以用它select非常容易地将任何对象配置成一个连续的数组。
处理数组数组的一种解决方案是修改 MyArray 以专门处理它,或者制作一个版本来处理它。
一个不同的解决方案是重新考虑您的方法,也许将所有数据添加到一个 MyArray 中,但将所有
objects1: [
0: { object : [4-5 assigned values (int, string, array)] },
1: { object : [4-5 assigned values (int, string, array)] },
//etc
]
},
{ objects2: [
0: {object},
1: {object},
//etc
]
}
变成漂亮的对象。
说真的,请阅读我在那些 link 中写的内容,那里有我对所有内容的推理。