javascript下过滤数组重复值的代码(类似于SQL的distinct)
|
admin
2012年4月23日 21:51
本文热度 12148
|
<script language="javascript"> function getNoRepeat() { return arguments[0].join('‖').match(/(\b[^‖]+\b)(?!.*‖\1\b)/ig); } var tmpArr = [1, 'a', 'ab', 'abc', 'd', 1.2, 'a+b', 'd', 'e', 5, 'a', 1, 'h', 'c', 'ab']; var retArr = getNoRepeat(tmpArr); alert(retArr); </script>
<script> alert("1,11,1.11,1111,111,11,1,1.11".match(/(\b\d+(?:\.\d+)?\b)(?!.*,\1\b)/g)) </script>
<script> alert("123450,0,1,2,5,3,2,12,4,1,1,123450".match(/(\b\d+\b)(?!.*,\1(,|$))/ig)) </script>
<script> alert("123450,0,1,2,5,3,2,12,4,1,1,123450".match(/(\b\d+\b)(?!(?:,[^,]+)*,\1(?:,|$))/ig)) </script>
<script> var s = "0,1,2,5,3,2,12,4,1,1,123450"; var sTmp = (","+s.split(",").reverse().join(",")+",").replace(/,([^,]+)(?=,.*,\1,)/ig, '').split(",").reverse().join(); sTmp = sTmp.substr(1, sTmp.length-2); alert(sTmp) </script>
<script> var strArr = "123450,0,1,2,5,3,2,12,4,1,1,123450".split(",") var str = "," for(i = 0; i < strArr.length; i++) { if(str.indexOf("," + strArr[i] + ",") == -1)str += strArr[i] + "," } alert(str.substring(1,str.length - 1)) </script>
该文章在 2012/4/23 21:51:03 编辑过
| |
全部评论15 |
|
admin
2012年4月23日 21:55
js数组中去除重复值
<script>
Array.prototype.del = function() {
var a = {}, c = [], l = this.length;
for (var i = 0; i < l; i++) {
var b = this[i];
var d = (typeof b) + b;
if (a[d] === undefined) {
c.push(b);
a[d] = 1;
}
}
return c;
}
alert([1, 1, 2, 3, 4, 5, 4, 3, 4, 4, 5, 5, 6, 7].del());
</script>
[Ctrl+A 全选 注:如需引入外部Js需刷新才能执行]
方法二
复制代码 代码如下:
//去重复数组
function unique(data){
data = data ││ [];
var a = {};
len = data.length;
for (var i=0; i<len;i++){
var v = data[i];
if (typeof(a[v]) == 'undefined'){
a[v] = 1;
}
};
data.length=0;
for (var i in a){
data[data.length] = i;
}
return data;
}
方法三
复制代码 代码如下:
var arr = ["123","123","123","123","sfsdf","123","345","123","123","345","456","567","sdc"];
var str = [];
for(var i = 0,len = arr.length;i < len;i++){
! RegExp(arr[i],"g").test(str.join(",")) && (str.push(arr[i]));
}
alert(str);
方法四
复制代码 代码如下:
var pureMulti1=function(arr){
var obj={};
var a = [];
for(var i=0,l=arr.length;iif(!((arr[i]+"") in obj)){
a.push(arr[i]);
}
obj[arr[i]]="";
}
return a;
} 该评论在 2012/4/23 21:56:04 编辑过
|
|
admin
2012年4月23日 21:56
百度面试时问的一道题目,蛮常规的,但是当时自己的回答挺差劲的。现在总结记录下~
代码如下:
题目:要求写一个函数,去掉给定数组中的重复值。
如:
传入数组 a = [0, 8, 5, 4, 78, 8, 90, 4, 'a', 'b', 'a'];
要求返回:[0,4,5,8,78,90,a,b]
对于这个题目,在面试之后也想了好多次,不过一直没能想出一个时间复杂度较低的方法。昨天下午在宿舍看《JavaScript语言精粹》看到一个书中的一段代码有所触发,于是在jsfiddle上测试了,成功。代码如下(完整版参见jsfiddle)
var getNR = function(src) {
src = src ││ [];
var res = {};
var curr = [];
var i, j = 0,temp, name;
for (i = 0; i < src.length; i++) {
temp = src[i];
if (res[temp]) {
//do noting
} else {
res[temp] = 1;
}
}
for (name in res) {
if (res.hasOwnProperty(name)) {
curr[j++] = name;
}
}
return curr;
};
总结一下我的思路:
思路一:将目标数组进行排序,然后依序删除重复的数组,但这样在删除重复元素的同时也改变数组原有元素的属性,明显是不符合要求的,del。
思路二:新建一个数组b,将a中的元素push到b中,但是在push之前检查该元素是否存在。这个时间复杂度是n*n,最简单也是最笨的办法。
思路三:跟思路二类似,不过充分利用了js对象的属性,新建一个空对象,将a中的元素作为属性添加到该对象中,在添加之前检测该属性是否已存在。全部添加完后将该对象的属性依序放到数组中,return
美团面试的题目中有一道这个题目的变种:
要求在Array类上添加一个方法,对于任意数组调用该方法后,去除该数组中的重复元素。
这个变种题考查的知识点多了些,还包括原型,this的理解等。 该评论在 2012/4/23 21:57:39 编辑过
|
|
admin
2012年4月23日 22:22
三个精彩的函数~~大家收到自己的函数库里去吧~
前两天做一个功能的时候,为Array扩展了三个方法,可以清除数组中的重复的值。这些值可以是字符串,或者对象~~或者其它。
在网上找了找其它的类同的函数~效率和实现方法都比较差~哈哈~
---------------
// 数据元素唯一化, 较慢但保持元素顺序
// 例: [1,2,3,2,1,8,1,6,2]
// 输出: [1,2,3,8,6]
Array.prototype.unique = function() {
for (var j, i=0, k=this.length; i<k; i++) {
for (j=i+1; j<k; j++) {
if (this[i]===this[j] && k--) this.splice(j, 1);
}
}
}
// 数据元素唯一化, 较快但不能保持元素顺序
// 例: [1,2,3,2,1,8,1,6,2]
// 输出: [1,2,3,8,6]
Array.prototype.q_unique = function() {
for (var j, i=0, k=this.length; i<k; i++) {
for (j=i+1; j<k; j++) {
if (this[i]===this[j]) this[i]=this[--k];
}
}
this.length = k;
}
// 数据元素唯一化, 仅用于排序过的数组(并保持排序状态)
// 例: [1,2,2,4,4,5,5]
// 输出: [1,2,4,5]
Array.prototype.s_unique = function() {
for (var j, m, i=0, k=this.length; i<k; i++, k-=m) {
for (j=i+1, m=0; j<k; j++, m++) {
if (this[i]!==this[j]) break;
}
this.splice(i, m);
}
} 该评论在 2012/4/23 22:23:57 编辑过
|
|
admin
2012年4月23日 22:24
- <script>
- //Aiming 的算法
- //---------------
- // 数据元素唯一化, 较慢但保持元素顺序
- // 例: [1,2,3,2,1,8,1,6,2]
- // 输出: [1,2,3,8,6]
- Array.prototype.unique = function() {
- for (var j, i=0, k=this.length; i<k; i++) {
- for (j=i+1; j<k; j++) {
- if (this[i]===this[j] && k--) this.splice(j, 1);
- }
- }
- }
- // 数据元素唯一化, 较快但不能保持元素顺序
- // 例: [1,2,3,2,1,8,1,6,2]
- // 输出: [1,2,3,8,6]
- Array.prototype.q_unique = function() {
- for (var j, i=0, k=this.length; i<k; i++) {
- for (j=i+1; j<k; j++) {
- if (this[i]===this[j]) this[i]=this[--k];
- }
- }
- this.length = k;
- }
- // 数据元素唯一化, 仅用于排序过的数组(并保持排序状态)
- // 例: [1,2,2,4,4,5,5]
- // 输出: [1,2,4,5]
- Array.prototype.s_unique = function() {
- for (var j, m, i=0, k=this.length; i<k; i++, k-=m) {
- for (j=i+1, m=0; j<k; j++, m++) {
- if (this[i]!==this[j]) break;
- }
- this.splice(i, m);
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////
- //Akira的处理方法,利用Hash
- function UniqueSet()
- {
- this.HashIndex = new Array();
- this.Collection = new Array();
- UniqueSet.prototype.Add = function(obj)
- {
- if (!(obj instanceof Object)) //如果obj不是对象,那么就按照基本数据类型(值类型)的方式处理,注意:可能对DOM无效
- {
- if (this.HashIndex[obj] == null)
- {
- this.HashIndex[obj] = this.Collection.length;
- this.Collection.push(obj);
- }
- }
- else if (obj.UUID != null) //如果对象定义了唯一表识UUID,则用UUID作为索引来处理
- {
- if (this.HashIndex[UUID] == null)
- {
- this.HashIndex[UUID] = this.Collection.length;
- this.Collection.push(obj);
- }
- }
- else
- {
- //对象的处理办法,利用脚本特性
- if (obj.__UniqueSet_Member_Belongs_To == null)
- obj.__UniqueSet_Member_Belongs_To = new Array();
- for (var i = 0; i < obj.__UniqueSet_Member_Belongs_To.length; i++)
- {
- if (obj.__UniqueSet_Member_Belongs_To[i] == this)
- {
- return;
- }
- }
- obj.__UniqueSet_Member_Belongs_To.push(this);
- this.Collection.push(obj);
- }
- }
- UniqueSet.prototype.toArray = function()
- {
- return this.Collection;
- }
- }
- UniqueSet.parse = function(array)
- {
- array = array.slice(0);
- var uSet = new UniqueSet();
-
- for (var i = 0; i < array.length; i++)
- {
- uSet.Add(array[i]);
- }
- return uSet.toArray();
- }
- function createObjs(size)
- {
- var objList = new Array(); //构造测试数组
- for (var i = 0; i < size; i++) //构造对象(这部分时间不计)
- {
- if (Math.random() > rate)
- {
- item = new Object();
- item.hashCode = Math.random();
- }
- objList.push(item);
- }
- return objList;
- }
- function uniqueTest(objList)
- {
- var test = objList.slice(0); //构造测试副本
- document.write("元素个数:"+test.length+",重复率:"+rate+",开始计时<br/>");
- var startTime = new Date().getTime(); //计时
- test.unique();
- var timeUsed = (new Date() - startTime)/1000;
- document.write("unique()耗时:"+timeUsed+"秒,不重复元素个数"+test.length+"个<br/>");
- }
- function q_uniqueTest(objList)
- {
- var test = objList.slice(0); //构造测试副本
- document.write("元素个数:"+test.length+",重复率:"+rate+",开始计时<br/>");
- var startTime = new Date().getTime(); //计时
- test.q_unique();
- var timeUsed = (new Date() - startTime)/1000;
- document.write("q_unique()耗时:"+timeUsed+"秒,不重复元素个数"+test.length+"个<br/>");
- }
- function uSetTest(objList)
- {
- var test = objList.slice(0);
- document.write("元素个数:"+test.length+",重复率:"+rate+",开始计时<br/>");
- var startTime = new Date().getTime(); //计时
- test = UniqueSet.parse(test);
- var timeUsed = (new Date() - startTime)/1000;
- document.write("uSet()耗时:"+timeUsed+"秒,不重复元素个数"+test.length+"个<br/><br/>");
- }
- //测试开始...
- var rate = 0.1; //元素重复率
- var item = new Object(); //要插入的元素
- item.hashCode = Math.random(); //没有别的意义,单纯的标识
- var objList = createObjs(500); //构造一个500随机对象的数组
- //下面开始测试排序:
- uniqueTest(objList);
- q_uniqueTest(objList);
- uSetTest(objList);
- objList = createObjs(800); //增加到800对象
- uniqueTest(objList);
- q_uniqueTest(objList);
- uSetTest(objList);
- objList = createObjs(1000); //增加到1000对象
- uniqueTest(objList);
- q_uniqueTest(objList);
- uSetTest(objList);
- document.write("后面因为Aiming的算法速度太慢,而IE提示会影响计时,所以不得不停止测试,仅用我的算法计时<br/>");
- //objList = createObjs(1200); //增加到1200对象
- //uSetTest(objList);
- objList = createObjs(1500); //增加到1500对象
- uSetTest(objList);
- document.write("很奇怪地发现我写的算法得到的结果和Aiming写的两个算法分别得到的结果居然都不一样??结果:随便测试了一下...<br/>");
- var testArray = new Array(10,20,30,30,30,40,20,10,4);
- test = testArray.slice(0);
- test.unique();
- document.write("居然检测出了Aiming的unique()函数的这个错误结果:<br/>原始数组:"+testArray+"<br/>结果数组:"+test+"<br/>=.=...汗...<br/>");
- </script>
复制代码运行代码另存代码
应该说,楼主写的函数从代码的角度来讲,还算是不错的,但是有下面的一些问题:
1) 没有保存原始的数组对象,既然改写(增加)了数组的方法,那么应该保护原始的数组对象,否则的话array.unique()的时候破坏掉了原来的数组。
像array.slice()等系统库函数这方面就做得比较好,楼主可以参考。
除非楼主是打算做成像array.push()或者array.splice()之类的“写操作”方法,不过我个人认为将unique方法做成写方法的话应用价值就打折扣了。
2)方法的效率不是很高,一个简单的测试如上...大概算到1000数量级的时候就不大行了。我利用脚本语言的特性写了实现同样功能的方法,效率会比较高一点。
脚本是解释型语言,弱点是效率比较低,但是同时也带来一些灵活的特性(例如动态增加属性),活用它们可以弥补弱点。
3)一个更为致命的问题是头两个方法都有BUG,第三个方法我没有测试,不知道是否正确。第一个方法(unique())居然连new Array(10,20,30,30,30,40,20,10,4);这样简单的Case都没有通过,建议楼主再修改一下。
最后我要说,脚本语言虽然简单,然而因为自由,使用起来也有各种复杂的变化。如何发挥它们的优势,弥补不足,其实这其中的道理并不简单。
作为程序语言的一种,脚本语言也是“道”的一部分。在求“道”的过程中应当多多深入探索,才会有所领悟,许多东西并不像表面那么简单。在我看来,楼主的代码也仅能算做勉强合格(去除BUG之后),所以我想劝楼主收回“精彩”二字以及后面那句话 :P
停止一些浮躁的举动,静下心来认真学习和研究,往往会有很多意想不到的收获。 该评论在 2012/4/23 22:26:54 编辑过
|
|
admin
2012年4月23日 22:25
对了,各位在运行上面那个例子的时候如果IE弹出了运行速度慢的提示的话,那么最后一个(也可能是倒数第二个,具体要看提示框弹出的时间)时间就不准确了...(因为记录的时间还包括点击确定按钮关闭对话框的时间)
其实主要还是因为测试用例动态创建随机大数组浪费了非常多的时间,如果单独使用的话,我的算法大概可以跑在高于10000数量级上,如果,数组元素是简单类型而不是对象的话,还可以再提高一个数量级,但那也不是最好的算法,相信还有很多个优秀的算法实现...
总体来说,javascript是非常自由的...
所以充分发挥想象和创造往往可以获得“令人惊叹”的代码...
如果真正能做到,是非常令人佩服的(那种大牛是月影追求的目标^^)
而能够欣赏到那样的代码也是非常幸运的
因为那也是一种程序界的“艺术”和“美” ^^
|
|
admin
2012年4月23日 22:25
好吧~既然有人讨论这个问题,那么就讨论下去。
两个方法中存在的BUG,我还没有来得及分析。先不论。
正如月影所说,我的确是需要保持array的引用,要不然我也不会把三个方法都写成那样。类似于push、shift、splice等方法,在array中保持引用的函数还是非常重要的。不能简单的认为那是“破坏了原有的数组”。素不如,有时候,保持数组引用比保证元素个数来得更复杂。
第二个点要讨论的就是月影的算法了。正好我也对算法比较感兴趣。
UniqueSet.prototype.Add()方法分三个部分,对不同的数据类型(或形式)做分别处理。
那么我们来看第一次筛选:
//如果obj不是对象,那么就按照基本数据类型(值类型)的方式处理
if (!(obj instanceof Object)) {}
的确,数据如果不是object,那么就是值类型。或者无值。列举一下,就是下面这些东西:
<script>
foo = function() {}; // 函数
arr = []; // 数组
str = 'this is test'; // 字符串
bool = false; // 布尔值
undef = void null; // undefined
obj = {};
testarr = [foo, arr, str, bool, undef, obj];
for (i=0; i<testarr.length; i++) {
document.writeln(testarr[i] instanceof Object, '<br>');
}
</script>
上面这个测试的结果,是说有string, boolean和undefined三种类型都不是instanceof Object的。那么接下来的代码
-------
this.HashIndex[obj] = this.Collection.length;
-------
中,这三种类型的数据会出现什么样的问题呢?
HashIndex[obj] = xxx这种用法会导致obj被强制转换成string,并以此来作为属性名。在js中,空字符串是可以用作属性名的(这多少看起来有点奇怪)。这中间的转换规则是:
<script>
str = 'this is test';
bool = false;
undef = void null;
obj = {};
testarr = [str, bool, undef];
for (i=0; i<testarr.length; i++) {
obj[testarr[i]] = null;
}
for (i in obj) {
document.writeln(i, '<br>');
}
</script>
也就是说,在这样的代码中,字符串'false','true'和'undefined'将与其它的boolean值、undefined值存在混淆。
这样的分析结果是,如果有一个数组:
['true','false','undefined',true,false,void(0)];
那么月影的方法测试出来就会有三个重复值。
而用我的方法则不会有这样的问题。 该评论在 2012/4/23 22:26:00 编辑过
|
|
admin
2012年4月23日 22:27
第二次筛选则使用了UUID。
//如果对象定义了唯一表识UUID,则用UUID作为索引来处理
if (obj.UUID != null) {}
En... 这个方法就是安全可靠的了。当然,如果这个对象在进入array之前就被定义了一个UUID的话。这可能会出现在一些本地数据库系统中。例如我们为每一个记录生成一个ID以便于检索。或者是因为其它目的而有意地在对象里加入UUID。
当然,前提是你必须与使用这段代码的人提前约定:为进入array的数据加上UUID。
|
|
admin
2012年4月23日 22:27
第三次筛选用了一个很异怪的方法,月影说是“利用脚本特性”。这样说是很含糊的。其实他的思路是说:如果一个对象的多个引用存在于数组中,那么从任何维取到的数据实际上都是这个引用,因而也就存在obj_ref['__UniqueSet_Member_Belongs_To']这个属性。
这个属性是随意定义的,写什么什么都无所谓,只要没人用就成。
由于一个对象可能被放到多个数组中参与unique的运算。因此月影把这个属性声明成数组,并保存一个当前排序用的UniqueSet的引用。
随后,他就可以用if (obj.__UniqueSet_Member_Belongs_To[i] == this)来检测这个对象obj是否已经在当前的排序用的UniqueSet()中被处理过一次。如果处理过,则pass掉。
思路是很奇特的。然而这里面存在一个不可调和矛盾:
1. 如果UniqueSet()是重复使用的,那么对一个已经处理过的数组,或者这个数组中的元素被添加到另一个数组中,那么这个数据在用UniqueSet()处理时将会失效。
2. 如果UniqueSet()是用过即弃的,那么~~你会看到每一个数组中对象的__UniqueSet_Member_Belongs_To数组长度将会因为多次使用UniqueSet()而不断增加,而且由于数组中总是保存着UniqueSet的引用,因此所有这个数组以及每次运行创建的UniqueSet实例uSet都得不到释放。
因此无论你用哪种方案来做选择,要么是速度不稳定,要么是准确性出问题。
以下面的代码作为例子:
// 1. 测试__UniqueSet_Member_Belongs_To数组长度因运行次数而增加
obj1 = {n1:1, n2:2};
obj2 = {n1:3, n2:4};
obj3 = {n1:3, n2:4};
uSetTest([obj1, obj2, obj3]);
uSetTest([obj1, obj2, obj3]);
uSetTest([obj1, obj2, obj3]);
document.writeln(obj1.__UniqueSet_Member_Belongs_To.length);
// 2. 测试使用同一个uSet实例的结果
obj1 = {n1:1, n2:2};
obj2 = {n1:3, n2:4};
obj3 = {n1:3, n2:4};
uSet = new UniqueSet();
function uSetTest2(array) {
// same UniqueSet.parse()
uSet.Collection.length = 0;
for (var i = 0; i < array.length; i++)
{
uSet.Add(array[i]);
}
test = uSet.toArray();
document.write("不重复元素个数"+test.length+"个 ");
}
uSetTest2([obj1, obj2, obj3]);
uSetTest2([obj1, obj2]);
uSetTest2([obj1]);
|
|
admin
2012年4月23日 22:28
整体来说~月影的三个算法都不是我所欣赏的。尤其是在对象上添加__UniqueSet_Member_Belongs_To这个属性,如果把这一个代码用在一个框架上,就意味着所有用for .. in的地方都必须能预知并处理这个属性。——我个人向来是不建议为Object添加自定义属性的。
算法是要耐得起读的。我没有用大量的数据来测试,只是分析了整个代码的原理,就找到这些漏洞,因此不得不说作者在算法的设计思路上不够细致。
我的代码中存在的BUG,想来还只是+1或者-1的问题吧。我想,今晚回家再测试测试。哈哈~~~
欢迎持续地、深入地讨论。
|
|
admin
2012年4月23日 22:28
第一个问题诚如你所说的...是有不完善的地方...=.=
不过['true','false','undefined',true,false,void(0)];这种应用的意义价值不大...
而且如果充分考虑的话,是可以避免的...
至于obj_ref['__UniqueSet_Member_Belongs_To'],uSet并不是像你的例子中那样通过构造对象来使用。注意到静态方法UniqueSet.parse(array),因为一个对象虽然可以属于多个集合,但是集合与数组必须满足一一对应的关系...
所以的确如你所说uSet的实例并不能重复使用
至于对象实例的释放问题,一般我是这样处理的,通过建立统一的“对象池”来管理对象的分配和释放,这是因为脚本语言在这方面并没有完善的“内置实现”(而我的项目中有严格的空间限制)
然而那一部分Code和对象的类型是无关的,而且比较复杂,所以我给的例子中并没有包括。
顺带着插一些题外话,因为最近的工作关系,正好遇到将大量数据发送到客户端进行分析的需求,所以比较关注这方面的技术。不过,正因为数据量非常大,所以算法的效率是非常关键的,而且,项目开发平台和测试平台对时间和空间都有近乎苛求的限制,所以有时候不得不用“非常规”的办法处理。所以楼主所谓的“不欣赏”月影的代码,月影也可以理解。但是月影在这里要表达的意思是:脚本是自由的,有时候突破常规思路可以开拓视野,会发现“原来代码也可以这么写”。这是其他程序员所难以体验到的,所以作为js coder从某种意义上来说也是幸运的(虽然我并不是全职的js coder,但是对这一点也深有体会^^)。
不论月影的代码在楼主看来有多少问题,我想至少有一点是应该承认的,那就是对于“重复元素处理”这样的问题,要获得高效率的算法,还是可以考虑采用这样的思路。(这种思路的时间复杂度为o(n))
另外,即使采用常规思路,也有一些时间复杂度(o(nlogn))的优秀算法可以利用,虽然它们比较复杂。
最后,说一下关于Hash的问题,很显然稍有数学基础的朋友很容易看出月影的算法本质上是一种Hash的思想,而不知为何楼主说月影的算法与hash无关,月影也无话可说,请大家自行判断吧=.=。
|
|
|