challenge アレイのuniq

アレイ(複数の値が配列状になっているもの)xsが与えられたときに、同じ値が2回以上出現しないように、2回目以降の出現を取り除いたアレイを返すコードを書いてください。

Rubyで表現すると下のようになります。

irb(main):001:0> xs = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9]
=> [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9]
irb(main):002:0> xs.uniq
=> [3, 1, 4, 5, 9, 2, 6, 8, 7]

間違えないように:よくある「ハッシュを使う」「集合オブジェクトを使う」などの方法は順番が乱れてしまうので使えません。出現順序を守りつつ、2回目以降の出現だけを取り除いてください。

この投稿は匿名での挑戦状の投稿を元に作成しています。ご投稿ありがとうございます。

Posted feedbacks - JavaScript

うーん。プリミティブ値だけの配列だったらこれでいける。
でも、オブジェクトとかが入ってくると 
toString() の結果によっては正しい結果にならないなあ。

alert([1, 2, 3, 3, 2, 1].uniq()); // [1, 2, 3]
1
2
3
4
5
6
7
Array.prototype.uniq = function () {
  for (var i = 0, r = [], s = {}; i < this.length; i++) {
    if (!s[this[i]]) r.push(this[i]);
    s[this[i]] = true;
  }
  return r;
};

Object Type にも対応した書き方。
効率悪いとか言わない><

alert([1, 2, {}, {}, 2, 1].uniq()); // [1, 2, {}, {}]

var a = {};
alert([1, 2, a, a].uniq()); // [1, 2, {}]
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Array.prototype.uniq = function () {
  for (var i = 0, r = [], s = []; i < this.length; i++) {
    for (var j = 0; j < s.length; j ++) 
      if (s[j] === this[i]) break;
    if (j == s.length) {
      r.push(this[i]);
      s.push(this[i]);
    }
  }
  return r;
};

forEachでまわしてる配列をいじるのってやばいですかね。
1
2
3
4
5
6
var xs = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9];
xs.forEach(function(i) {
    if (xs.indexOf(i) != xs.lastIndexOf(i))
        xs.splice(xs.lastIndexOf(i), 1)
});
alert(xs);

こんな感じかな? spidermonkey1.6で確認。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Array.prototype.uniq = function() {                                                                                                    
  label_unique:                                                                                                                        
  for(var uniformized = [], i = 0; i < this.length; i++) {                                                                             
    for(var l = 0; l < uniformized.length; l++) if(this[i] === uniformized[l]) continue label_unique;                                  
    uniformized.push(this[i]);                                                                                                         
  }                                                                                                                                    
  return uniformized;                                                                                                                  
}                                                                                                                                      
var xs = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9];                                                                                
xs.uniq(); // 3,1,4,5,9,2,6,8,7 

効率の方を。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
Array.prototype.uniq = function () {
  var result = [], prev = {}, value, prev_null = 0, i;
  for (i = 0; i < this.length; ++i) {
    value = this[i];
    typeof value !== 'object'
      ? prev[value] || (prev[value] = 1, result.push(value))
      : value === null
        ? prev_null++ || result.push(null)
        : value === void(0) || value.__prev || (value.__prev = true, result.push(value));
  }
  for (i = 0; i < this.length; ++i) {
    value instanceof Object && delete value.__prev;
  }
  return result;
};

いっそ両方とも実装してみるとか。ダメ?
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Array.prototype.uniq = function(usedObjKey) {
  if (typeof(usedObjKey) == 'undefined')
    usedObjKey = (typeof(this[0]) == 'object');
  
  var ret = [];
  if (usedObjKey) {
    for (var i=0, len=this.length, s=[]; i<len; i++) {
      if (s.indexOf(this[i]) < 0) {
        ret.push(this[i]);
        s.push(this[i]);
      }
    }
  }
  else
  {
    for (var i=0, len=this.length, s={}; i<len; i++) {
      if (!s[this[i]])
        ret.push(this[i]);
      s[this[i]] = true;
    }
  }
  return ret;
}

/**
 * @site http://developer.mozilla.org/ja/docs/
 *       Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf
 */
if (!Array.prototype.indexOf) {
  Array.prototype.indexOf = function(elt /*, from*/) {
    var len = this.length;
    var from = Number(arguments[1]) || 0;
    from = (from < 0) ? Math.ceil(from) : Math.floor(from);
    if (from < 0)
      from += len;
    
    for (; from<len; from++)
      if (from in this && this[from] === elt)
        return from;
    return -1;
  };
}

ObjectやFunctionにも対応してみた。firefox2.0.0.4+Firebugで確認。
なんというかtoJSONString()の手抜き実装みたいだ。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Array.prototype.uniq=function(){
	var h={};
	var tostr=function(obj){
		if(typeof obj == "object"){
			var str="";
			for(var p in obj){
				str+=p+":"+arguments.callee(obj[p]);
			}
			return "{"+str+"}";
		}
		return obj;
	}
	for(var i=0,h={},l=this.length,r=[]; chk=tostr(this[i]),i<l; i++){
		if(!h[chk]===true)r.push(this[i]);
		h[chk]=true;
	}
	return r;
}

// -------------------

var a=[1,2,[3,4],6,2,3,{},{},[4,3]
	,function(){},function(a){},function(){},function(){return this;}
	,{"a":"test1"}
	,{"a":"nest","b":{"c":"nest2"}}
	,{"a":"nest2","b":{"c":"nest3"}}
	,{"a":"nest","b":{"c":"nest2"}}
	,{"a":"test1"}
	,{"a":"test3"}
	,[5,12,4],[4,3],[5]
];
a.uniq();

//[1, 2, [3, 4], 6, 3, Object, [4, 3], function(), function(), function()
//, Object a=test1, Object a=nest b=Object, Object a=nest2 b=Object, Object a=test3
//, [5, 12, 4], [5]]

Index

Feed

Other

Link

Pathtraq

loading...