I am wondering how I can check if a duplicate pair of values in an array exist as part of a larger array in javascript. You can see there is a duplicate pair of [1,2] - so the function should just return true. i.e
var arr = [[1,2], [3,4], [5,6], [7,8], [9,10], [11,12], [13,14], [1,2]]
I have tried using this logic which gives me a clean array and a "true"
var unique = [];
var done = []; var dup = false;
for(var x = 0; x < arr.length; x++) {
var myStr = arr[x].toString();
if(done.indexOf(myStr) != -1) {
// val already exist, ignore
dup = true;
continue;
}
done.push(myStr);
unique.push(arr[x]);
}
But I was wondering if there is something more elegant using Underscore ?
The shortest way would be to use _.uniq and JSON.stringify:
function unique(arr) {
return _.uniq(arr, JSON.stringify).length === arr.length;
}
But that doesn't short-circuit, so it's somewhat slow compared to the other ways you could do it. Tomalak's second function should be faster.
Well, uniq seems like a good fit
function containsDuplicates(arr) {
return arr.length !== _.uniq(arr, function (item) { return item.toString(); }).length;
}
You should use Blender's version of this function. It's shorter and safer.
BTW, your code should look more like this:
function containsDuplicates(arr) {
var index = {}, i, str;
for(i = 0; i < arr.length; i++) {
// you could use arr[i].toString() here, but JSON.stringify()
// is a lot safer because it cannot create ambiguous output.
str = JSON.stringify(arr[i]);
if (index.hasOwnProperty(str)) {
return true;
} else {
index[str] = true;
}
}
return false;
}
Note that this is probably more efficient than the underscore one-liner.
Although stringify is the answer most of the time, it still has its issues, for example {"x":1,"y":2} and {"y":2,"x":1} are considered different. If you need a 100% accurate comparison, there's no other way as to store already processed objects and deep compare them (luckily, underscore provides an utility for this).
uniq2 = function(xs) {
return _.reduce(xs, function(result, x) {
if(!_.any(result, _.partial(_.isEqual, x)))
result.push(x);
return result;
}, []);
}
Test:
var arr = [[1,2], [3,4], "1,2", "[1,2]", [1,2], {x:1,y:2}, {y:2,x:1}]
console.log(uniq2(arr))
// [[1,2],[3,4],"1,2","[1,2]",{"x":1,"y":2}]
This is going to be quadratic in the worst case, but there's no other way.
Related
Let's say I have a Javascript associative array (a.k.a. hash, a.k.a. dictionary):
var a = new Array();
a['b'] = 1;
a['z'] = 1;
a['a'] = 1;
How can I iterate over the keys in sorted order? If it helps simplify things, I don't even need the values (they're all just the number 1).
You can use the Object.keys built-in method:
var sorted_keys = Object.keys(a).sort()
(Note: this does not work in very old browsers not supporting EcmaScript5, notably IE6, 7 and 8. For detailed up-to-date statistics, see this table)
You cannot iterate over them directly, but you can find all the keys and then just sort them.
var a = new Array();
a['b'] = 1;
a['z'] = 1;
a['a'] = 1;
function keys(obj)
{
var keys = [];
for(var key in obj)
{
if(obj.hasOwnProperty(key))
{
keys.push(key);
}
}
return keys;
}
keys(a).sort(); // ["a", "b", "z"]
However there is no need to make the variable 'a' an array. You are really just using it as an object and should create it like this:
var a = {};
a["key"] = "value";
you could even prototype it onto object:
Object.prototype.iterateSorted = function(worker)
{
var keys = [];
for (var key in this)
{
if (this.hasOwnProperty(key))
keys.push(key);
}
keys.sort();
for (var i = 0; i < keys.length; i++)
{
worker(this[ keys[i] ]);
}
}
and the usage:
var myObj = { a:1, b:2 };
myObj.iterateSorted(function(value)
{
alert(value);
}
I agree with Swingley's answer, and I think it is an important point a lot of these more elaborate solutions are missing. If you are only concerned with the keys in the associative array and all the values are '1', then simply store the 'keys' as values in an array.
Instead of:
var a = { b:1, z:1, a:1 };
// relatively elaborate code to retrieve the keys and sort them
Use:
var a = [ 'b', 'z', 'a' ];
alert(a.sort());
The one drawback to this is that you can not determine whether a specific key is set as easily. See this answer to javascript function inArray for an answer to that problem. One issue with the solution presented is that a.hasValue('key') is going to be slightly slower than a['key']. That may or may not matter in your code.
There's no concise way to directly manipulate the "keys" of a Javascript object. It's not really designed for that. Do you have the freedom to put your data in something better than a regular object (or an Array, as your sample code suggests)?
If so, and if your question could be rephrased as "What dictionary-like object should I use if I want to iterate over the keys in sorted order?" then you might develop an object like this:
var a = {
keys : new Array(),
hash : new Object(),
set : function(key, value) {
if (typeof(this.hash[key]) == "undefined") { this.keys.push(key); }
this.hash[key] = value;
},
get : function(key) {
return this.hash[key];
},
getSortedKeys : function() {
this.keys.sort();
return this.keys;
}
};
// sample use
a.set('b',1);
a.set('z',1);
a.set('a',1);
var sortedKeys = a.getSortedKeys();
for (var i in sortedKeys) { print(sortedKeys[i]); }
If you have no control over the fact that the data is in a regular object, this utility would convert the regular object to your fully-functional dictionary:
a.importObject = function(object) {
for (var i in object) { this.set(i, object); }
};
This was a object definition (instead of a reusable constructor function) for simplicity; edit at will.
Get the keys in the first for loop, sort it, use the sorted result in the 2nd for loop.
var a = new Array();
a['b'] = 1;
a['z'] = 1;
a['a'] = 1;
var b = [];
for (k in a) b.push(k);
b.sort();
for (var i = 0; i < b.length; ++i) alert(b[i]);
You can use the keys function from the underscore.js library to get the keys, then the sort() array method to sort them:
var sortedKeys = _.keys(dict).sort();
The keys function in the underscore's source code:
// Retrieve the names of an object's properties.
// Delegates to **ECMAScript 5**'s native `Object.keys`
_.keys = nativeKeys || function(obj) {
if (obj !== Object(obj)) throw new TypeError('Invalid object');
var keys = [];
for (var key in obj) if (_.has(obj, key)) keys.push(key);
return keys;
};
// Shortcut function for checking if an object has a given property directly
// on itself (in other words, not on a prototype).
_.has = function(obj, key) {
return hasOwnProperty.call(obj, key);
};
<script type="text/javascript">
var a = {
b:1,
z:1,
a:1
}; // your JS Object
var keys = [];
for (key in a) {
keys.push(key);
}
keys.sort();
var i = 0;
var keyslen = keys.length;
var str = '';
//SORTED KEY ITERATION
while (i < keyslen) {
str += keys[i] + '=>' + a[keys[i]] + '\n';
++i;
}
alert(str);
/*RESULT:
a=>1
b=>1
z=>1
*/
</script>
var a = new Array();
a['b'] = 1;
a['z'] = 1;
a['a'] = 1;
var keys=Object.keys(a).sort();
for(var i=0,key=keys[0];i<keys.length;key=keys[++i]){
document.write(key+' : '+a[key]+'<br>');
}
I really like #luke-schafer's prototype idea, but also hear what he is saying about the issues with prototypes. What about using a simple function?
function sortKeysAndDo( obj, worker ) {
var keys = Object.keys(obj);
keys.sort();
for (var i = 0; i < keys.length; i++) {
worker(keys[i], obj[keys[i]]);
}
}
function show( key, value ) {
document.write( key + ' : ' + value +'<br>' );
}
var a = new Array();
a['b'] = 1;
a['z'] = 1;
a['a'] = 1;
sortKeysAndDo( a, show);
var my_object = { 'c': 3, 'a': 1, 'b': 2 };
sortKeysAndDo( my_object, show);
This seems to eliminate the issues with prototypes and still provide a sorted iterator for objects. I am not really a JavaScript guru, though, so I'd love to know if this solution has hidden flaws I missed.
I'm trying to create an Array with unique objects. I've got json data from a tounament that I want to order by pools. Each match got his own pooldata so he will push the pool data of each match to the array. This will create an Array of 5 of the same pool values. This is my code:
var arr = [];
for (var i = 0; i < data.objects.length; i++){
obj = {
poolId: data.objects[i].pool.id,
poolLetter: data.objects[i].pool.name
};
if (arr.indexOf(obj) == -1) {
arr.push(obj);
}else{}
}
The problem is that the obj you are generating in the loop is not going to be the same object inside your arr array, they will have different signatures, so you can't use indexOf in this instance.
Instead, you will have to loop over the arr array to see if you can find any elements with the same poolId or poolLetter:
var arr = [];
for (var i = 0; i < data.objects.length; i++){
obj = {
poolId: data.objects[i].pool.id,
poolLetter: data.objects[i].pool.name
};
// Do the insert if arr doesn't already contain this poolId
if(!arrayContains(arr, obj.poolId)) arr.push(obj);
}
// Helper function to find an instance of poolId in the given array
function arrayContains(arr, poolId) {
for(var x = 0; x < arr.length; x++) {
if(arr[x].poolId === poolId) {
return true;
}
}
return false;
}
Here is a fiddle which demonstrates the above.
indexOf compares searchElement to elements of the Array using strict equality (the same method used by the ===, or triple-equals, operator).
then
var obj1 = { a:1};
var obj2 = { a:1};
obj1 === obj2; // wrong
when you write "var obj1={a:1}" ,javascript create a new object.
You can use Array Prototype. Just pass the object.
Array.prototype.contains = function(obj) {
var i = this.length;
while (i--) {
if (this[i] === obj) {
return true;
}
}
return false;
}
Use the following
alert([1, 2, 3].contains(2)); //true
alert([1, 2, 3].contains('2')); //false
There is also a jQuery solution. I know you didn't asked for a jQuery answer. But maybe you want use it.
jQuery.inArray() returns the index of a specified value and returns -1 if not found.
http://api.jquery.com/jQuery.inArray/
I'm working on project where I have array simmilar to this
var sortBp = [ 'height58em', 'width480px', 'width768px', 'width959px', 'width767px', 'width767px' ];
I want to sort this array in ascending order while eleminating repeated values so that result should be
var sortBp = [ 'height58em', 'width480px', 'width767px', 'width768px', 'width959px' ];
I'm using following function to sort array in ascending array but how do I eliminate immediate values ?? (In above case 'width767px')
var sortBp = bpArrays.sort(function(a, b) {
a = a.replace(/[a-z]/g, '');
b = b.replace(/[a-z]/g, '');
return a - b;
});
Firstly, you can't eliminate elements while sorting. You have to sort the array first, then remove duplicates. Your solution using Array.prototype.filter and Array.prototype.indexOf might be good for unsorted array, but since your array is sorted, it's overhead here(takes O(n) for each). Instead you can just loop through the array and compare element with previous one.
function uniq(array) {
var i, l = array.length, result = [];
for (i = 0; i < l; i++) {
if (result[result.length - 1] != array[i]) {
result.push(array[i]);
}
}
return result;
}
This is same with Underscore and Prototype's uniq() implemention.
Last note: remember that this will work fine with sorted array only.
If this is not homework, i.e. you aren't required to implement the algorithm yourself, you could use a combination of sortBy and uniq functions.
sortBp = [ 'height58em', 'width480px', 'width768px', 'width959px', 'width767px', 'width767px' ];
_.uniq(_.sortBy(sortBp, function(x) {return x;}), true);
This returns:
["height58em", "width480px", "width767px", "width768px", "width959px"]
Note that this will be sort by lexical order, and if you do not wish this to happen, simply substitute the identity function function(x) {return x;} with your own logic.
Array.prototype.contains = function(k) {
for ( var p in this)
if (this[p] === k)
return true;
return false;
};
inputArray.sort();
function uniqueArray(inputArray){
result=[];
for(var i in inputArray){
if(!result.contains(inputArray[i])){
result.push(inputArray[i]);
}
}
return result;
}
Found It !!
var sortBp = [ 'height58em', 'width480px', 'width768px', 'width959px', 'width767px', 'width767px' ];
uniqueArray = sortBp.filter(function(elem, pos) {
return sortBp.indexOf(elem) == pos;
});
EDIT :
I have found another solution here : http://dreaminginjavascript.wordpress.com/2008/08/22/eliminating-duplicates/
function eliminateDuplicates(arr) {
var i, len=arr.length, out=[], obj={};
for (i=0;i<len;i++) {
obj[arr[i]]=0;
}
for (i in obj) {
out.push(i);
}
return out;
}
I just wanted to know if anyone knew of a simple function to check if a given array is multidimensional and, if possible, also see how many dimensions it has.
Here is a sample array:
{"name":"Tiradentes","id":"234534gfgdgd4y5hy","areas":{{"name":"Apucarana", "id":"fh4oth98f894nf89h4"},{etc..., etc....}}}
Quite (I suppose) simple function counting dimensions BOTH in Arrays and array-like Objects (like in your case):
function countDimensions(arr)
{
if (! (arr && typeof arr === 'object') ) {
return 0;
}
var maxCount = Math.max.apply(null, $.map(arr, function(n) {
return countDimensions(n);
}));
return 1 + (isFinite(maxCount) ? maxCount : 0);
}
jQuery is used, as 0) it's mentioned in tags, 1) naive checking for Array with instanceof Array can lead to tricky results in multiframe apps, 2) $.map is a convenient shortcut for moving through all the array values.
It can be rewritten in plain JS, of course, but it won't be as compact. )
Had to use isFinite check, as protomax-ing an empty array gives -Infinite.
try with this
var dims = 0;
function getArrayDimensions(arr) {
if(typeof(arr) != 'array') return false;
for(var i = 0; i < arr.length; i++) {
if(typeof(arr[i]) == 'array') dims++;
dims += getArrayDimensions(arr[i]);
}
return dims;
}
JavaScript arrays are not really multi-dimensional, they are lists that can consist of different data types. So you only can determine the depth of an array by testing how often your array contains another array:
var isArray = Array.isArray || function(a) {
return a instanceof Array || Object.prototype.toString.call(a) == "[object Array]";
};
function getDepth(o) {
for (var i=0; isArray(o); o=o[0])
i++;
return i;
}
A more restrictive implementation would count the levels on which all slots of the array contain other arrays, instead of only inspecting the first one. Results would differ for [[], 0] for example.
function getDepth(o) {
if (!isArray(o))
return 0;
return 1 + (o.length && Math.min.apply(null, o.map(getDepth)));
}
I have an array like this:
[{prop1:"abc",prop2:"qwe"},{prop1:"bnmb",prop2:"yutu"},{prop1:"zxvz",prop2:"qwrq"},...]
How can I get the index of the object that matches a condition, without iterating over the entire array?
For instance, given prop2=="yutu", I want to get index 1.
I saw .indexOf() but think it's used for simple arrays like ["a1","a2",...]. I also checked $.grep() but this returns objects, not the index.
As of 2016, you're supposed to use Array.findIndex (an ES2015/ES6 standard) for this:
a = [
{prop1:"abc",prop2:"qwe"},
{prop1:"bnmb",prop2:"yutu"},
{prop1:"zxvz",prop2:"qwrq"}];
index = a.findIndex(x => x.prop2 ==="yutu");
console.log(index);
It's supported in Google Chrome, Firefox and Edge. For Internet Explorer, there's a polyfill on the linked page.
Performance note
Function calls are expensive, therefore with really big arrays a simple loop will perform much better than findIndex:
let test = [];
for (let i = 0; i < 1e6; i++)
test.push({prop: i});
let search = test.length - 1;
let count = 100;
console.time('findIndex/predefined function');
let fn = obj => obj.prop === search;
for (let i = 0; i < count; i++)
test.findIndex(fn);
console.timeEnd('findIndex/predefined function');
console.time('findIndex/dynamic function');
for (let i = 0; i < count; i++)
test.findIndex(obj => obj.prop === search);
console.timeEnd('findIndex/dynamic function');
console.time('loop');
for (let i = 0; i < count; i++) {
for (let index = 0; index < test.length; index++) {
if (test[index].prop === search) {
break;
}
}
}
console.timeEnd('loop');
As with most optimizations, this should be applied with care and only when actually needed.
How can I get the index of the object tha match a condition (without iterate along the array)?
You cannot, something has to iterate through the array (at least once).
If the condition changes a lot, then you'll have to loop through and look at the objects therein to see if they match the condition. However, on a system with ES5 features (or if you install a shim), that iteration can be done fairly concisely:
var index;
yourArray.some(function(entry, i) {
if (entry.prop2 == "yutu") {
index = i;
return true;
}
});
That uses the new(ish) Array#some function, which loops through the entries in the array until the function you give it returns true. The function I've given it saves the index of the matching entry, then returns true to stop the iteration.
Or of course, just use a for loop. Your various iteration options are covered in this other answer.
But if you're always going to be using the same property for this lookup, and if the property values are unique, you can loop just once and create an object to map them:
var prop2map = {};
yourArray.forEach(function(entry) {
prop2map[entry.prop2] = entry;
});
(Or, again, you could use a for loop or any of your other options.)
Then if you need to find the entry with prop2 = "yutu", you can do this:
var entry = prop2map["yutu"];
I call this "cross-indexing" the array. Naturally, if you remove or add entries (or change their prop2 values), you need to update your mapping object as well.
What TJ Crowder said, everyway will have some kind of hidden iteration, with lodash this becomes:
var index = _.findIndex(array, {prop2: 'yutu'})
var CarId = 23;
//x.VehicleId property to match in the object array
var carIndex = CarsList.map(function (x) { return x.VehicleId; }).indexOf(CarId);
And for basic array numbers you can also do this:
var numberList = [100,200,300,400,500];
var index = numberList.indexOf(200); // 1
You will get -1 if it cannot find a value in the array.
var index;
yourArray.some(function (elem, i) {
return elem.prop2 === 'yutu' ? (index = i, true) : false;
});
Iterate over all elements of array.
It returns either the index and true or false if the condition does not match.
Important is the explicit return value of true (or a value which boolean result is true). The single assignment is not sufficient, because of a possible index with 0 (Boolean(0) === false), which would not result an error but disables the break of the iteration.
Edit
An even shorter version of the above:
yourArray.some(function (elem, i) {
return elem.prop2 === 'yutu' && ~(index = i);
});
Using Array.map() and Array.indexOf(string)
const arr = [{
prop1: "abc",
prop2: "qwe"
}, {
prop1: "bnmb",
prop2: "yutu"
}, {
prop1: "zxvz",
prop2: "qwrq"
}]
const index = arr.map(i => i.prop2).indexOf("yutu");
console.log(index);
The best & fastest way to do this is:
const products = [
{ prop1: 'telephone', prop2: 996 },
{ prop1: 'computadora', prop2: 1999 },
{ prop1: 'bicicleta', prop2: 995 },
];
const index = products.findIndex(el => el.prop2 > 1000);
console.log(index); // 1
I have seen many solutions in the above.
Here I am using map function to find the index of the search text in an array object.
I am going to explain my answer with using students data.
step 1: create array object for the students(optional you can create your own array object).
var students = [{name:"Rambabu",htno:"1245"},{name:"Divya",htno:"1246"},{name:"poojitha",htno:"1247"},{name:"magitha",htno:"1248"}];
step 2: Create variable to search text
var studentNameToSearch = "Divya";
step 3: Create variable to store matched index(here we use map function to iterate).
var matchedIndex = students.map(function (obj) { return obj.name; }).indexOf(studentNameToSearch);
var students = [{name:"Rambabu",htno:"1245"},{name:"Divya",htno:"1246"},{name:"poojitha",htno:"1247"},{name:"magitha",htno:"1248"}];
var studentNameToSearch = "Divya";
var matchedIndex = students.map(function (obj) { return obj.name; }).indexOf(studentNameToSearch);
console.log(matchedIndex);
alert("Your search name index in array is:"+matchedIndex)
You can use the Array.prototype.some() in the following way (as mentioned in the other answers):
https://jsfiddle.net/h1d69exj/2/
function findIndexInData(data, property, value) {
var result = -1;
data.some(function (item, i) {
if (item[property] === value) {
result = i;
return true;
}
});
return result;
}
var data = [{prop1:"abc",prop2:"qwe"},{prop1:"bnmb",prop2:"yutu"},{prop1:"zxvz",prop2:"qwrq"}]
alert(findIndexInData(data, 'prop2', "yutu")); // shows index of 1
function findIndexByKeyValue(_array, key, value) {
for (var i = 0; i < _array.length; i++) {
if (_array[i][key] == value) {
return i;
}
}
return -1;
}
var a = [
{prop1:"abc",prop2:"qwe"},
{prop1:"bnmb",prop2:"yutu"},
{prop1:"zxvz",prop2:"qwrq"}];
var index = findIndexByKeyValue(a, 'prop2', 'yutu');
console.log(index);
Try this code
var x = [{prop1:"abc",prop2:"qwe"},{prop1:"bnmb",prop2:"yutu"},{prop1:"zxvz",prop2:"qwrq"}]
let index = x.findIndex(x => x.prop1 === 'zxvz')
Another easy way is :
function getIndex(items) {
for (const [index, item] of items.entries()) {
if (item.prop2 === 'yutu') {
return index;
}
}
}
const myIndex = getIndex(myArray);
Georg have already mentioned ES6 have Array.findIndex for this.
And some other answers are workaround for ES5 using Array.some method.
One more elegant approach can be
var index;
for(index = yourArray.length; index-- > 0 && yourArray[index].prop2 !== "yutu";);
At the same time I will like to emphasize, Array.some may be implemented with binary or other efficient searching technique. So, it might perform better over for loop in some browser.
Why do you not want to iterate exactly ? The new Array.prototype.forEach are great for this purpose!
You can use a Binary Search Tree to find via a single method call if you want. This is a neat implementation of BTree and Red black Search tree in JS - https://github.com/vadimg/js_bintrees - but I'm not sure whether you can find the index at the same time.
One step using Array.reduce() - no jQuery
var items = [{id: 331}, {id: 220}, {id: 872}];
var searchIndexForId = 220;
var index = items.reduce(function(searchIndex, item, index){
if(item.id === searchIndexForId) {
console.log('found!');
searchIndex = index;
}
return searchIndex;
}, null);
will return null if index was not found.
var list = [
{prop1:"abc",prop2:"qwe"},
{prop1:"bnmb",prop2:"yutu"},
{prop1:"zxvz",prop2:"qwrq"}
];
var findProp = p => {
var index = -1;
$.each(list, (i, o) => {
if(o.prop2 == p) {
index = i;
return false; // break
}
});
return index; // -1 == not found, else == index
}