Element with largest numeric index - javascript

How do I find the last element in a JavaScript object, if by "last" I consider the one with the largest index? I.e. I am receiving an object similar to an array over ajax (as json), but it also has a non-numeric key:
var received = {
0: 'something',
1: 'something else',
2: 'some stuff',
3: 'some more stuff',
'crap' : 'this screws the whole thing'
}
If it were a plain array, I would use array.length. Similarly, I can simply iterate element by element to find it.
Is there a better way? If jQuery is needed for the solution, that's also fine.

This works like the codes from zzzzBov and David Müller, but uses library functions do make it much shorter:
Math.max.apply(null, Object.keys(test).filter(isFinite)) // 3
If you have objects with enumerably-extended prototype objects (which is not the case for JSON.parse results) you might want to use Object.getOwnPropertyNames instead.

This is not an array, it's an object literal. Moreover, the order of items in object literal is not guaranteed to be preserved after creation (although most browsers seem to obey that).
That being said, generally the answer to your question is: you can't. What you can do is iterating over all properties and finding the last one or whichever you are interested in (see: How to Loop through plain JavaScript object with objects as members?). But remember that the order in which the properties are declared does not have to be preserved during iteration.
However, if this was a true array (disregarding the 'crap' key), that's pretty easy:
received = [
'something',
'something else',
'some stuff',
'some more stuff'
];
var last = received[received.length - 1];

With arrays (which are created with [], not {}), the length property is one more than the last index assigned. So:
var a = [];
a[0] = 'something';
a[5] = 'else';
a["foo"] = 'bar';
console.log(a.length);
will print "6", even though there are only two elements of the array and even though the foo property was set to 'bar'. The length property is only affected by numeric indexes.

var received = {
0: 'something',
2: 'some stuff',
3: 'some more stuff',
1: 'something else',
'crap' : 'this screws the whole thing'
};
var prop,
max = -1;
for ( prop in received ) {
if ( Object.prototype.hasOwnProperty.call(received, prop) && !isNaN(prop) ) {
max = Math.max(max, prop);
}
}
console.log(max);

This is of course not the most elegant approach, but you don't have another choice if have to stick to the literal
<script>
var received = {
0: 'something',
1: 'something else',
2: 'some stuff',
3: 'some more stuff',
'crap' : 'this screws the whole thing'
};
var max = null;
for (var i in received)
{
if (received.hasOwnProperty(i) && !isNaN(i))
{
if (max == null || i > max)
max = i;
}
}
if (max)
alert(received[max]); //some more stuff
</script>

If you want to know which item has the greatest numeric index value, you'd have to iterate over all the keys in the object:
(function () {
var i,
o,
temp;
o = getYourObject();
temp = -Infinity; //lowest possible value
for (i in o) {
if (o.hasOwnProperty(i) &&
!isNaN(i) &&
+i > temp) {
temp = +i;
}
}
console.log(temp); //this is the key with the greatest numeric value
}());

Related

Check if a value exist in a multidimensional array and remove it [duplicate]

I have an array like
vendors = [{
Name: 'Magenic',
ID: 'ABC'
},
{
Name: 'Microsoft',
ID: 'DEF'
} // and so on...
];
How do I check this array to see if "Magenic" exists? I don't want to loop, unless I have to. I'm working with potentially a couple thousand records.
No need to reinvent the wheel loop, at least not explicitly (using arrow functions, modern browsers only):
if (vendors.filter(e => e.Name === 'Magenic').length > 0) {
/* vendors contains the element we're looking for */
}
or, better yet, use some as it allows the browser to stop as soon as one element is found that matches, so it's going to be faster:
if (vendors.some(e => e.Name === 'Magenic')) {
/* vendors contains the element we're looking for */
}
or the equivalent (in this case) find:
if (vendors.find(e => e.Name === 'Magenic')) {
/* same result as above, but a different function return type */
}
And you can even get the position of that element by using findIndex:
const i = vendors.findIndex(e => e.Name === 'Magenic');
if (i > -1) {
/* vendors contains the element we're looking for, at index "i" */
}
And if you need compatibility with lousy browsers then your best bet is:
if (vendors.filter(function(e) { return e.Name === 'Magenic'; }).length > 0) {
/* vendors contains the element we're looking for */
}
2018 edit: This answer is from 2011, before browsers had widely supported array filtering methods and arrow functions. Have a look at CAFxX's answer.
There is no "magic" way to check for something in an array without a loop. Even if you use some function, the function itself will use a loop. What you can do is break out of the loop as soon as you find what you're looking for to minimize computational time.
var found = false;
for(var i = 0; i < vendors.length; i++) {
if (vendors[i].Name == 'Magenic') {
found = true;
break;
}
}
No loop necessary. Three methods that come to mind:
Array.prototype.some()
This is the most exact answer for your question, i.e. "check if something exists", implying a bool result. This will be true if there are any 'Magenic' objects, false otherwise:
let hasMagenicVendor = vendors.some( vendor => vendor['Name'] === 'Magenic' )
Array.prototype.filter()
This will return an array of all 'Magenic' objects, even if there is only one (will return a one-element array):
let magenicVendors = vendors.filter( vendor => vendor['Name'] === 'Magenic' )
If you try to coerce this to a boolean, it will not work, as an empty array (no 'Magenic' objects) is still truthy. So just use magenicVendors.length in your conditional.
Array.prototype.find()
This will return the first 'Magenic' object (or undefined if there aren't any):
let magenicVendor = vendors.find( vendor => vendor['Name'] === 'Magenic' );
This coerces to a boolean okay (any object is truthy, undefined is falsy).
Note: I'm using vendor["Name"] instead of vendor.Name because of the weird casing of the property names.
Note 2: No reason to use loose equality (==) instead of strict equality (===) when checking the name.
The accepted answer still works but now we have an ECMAScript 6 native methods [Array.find][1] and [Array.some][2] to achieve the same effect.
Array.some
Use some If you only want to determine if an element exists i.e. you need a true/false determination.
Quoting MDN:
The some() method tests whether at least one element in the array passes the test implemented by the provided function. It returns true if, in the array, it finds an element for which the provided function returns true; otherwise it returns false. It doesn't modify the array.
Array.find
Use find if you want to get the matched object from array else returns undefined.
Quoting MDN:
The find() method returns the value of the first element in the provided array that satisfies the provided testing function. If no values satisfy the testing function, undefined is returned.
var arr = [
{
id: 21,
label: 'Banana',
},
{
id: 22,
label: 'Apple',
}
]
/* note : data is the actual object that matched search criteria
or undefined if nothing matched */
var data = arr.find(function(ele) {
return ele.id === 21;
});
if (data) {
console.log('found');
console.log(data); // This is entire object i.e. `item` not boolean
}
/* note : doesExist is a boolean thats true or false depending on of whether the data was found or not */
var doesExist = arr.some(function(ele) {
return ele.id === 21;
});
See my jsfiddle link There is a polyfill for IE provided by mozilla
Here's the way I'd do it
const found = vendors.some(item => item.Name === 'Magenic');
array.some() method checks if there is at least one value in an array that matches criteria and returns a boolean.
From here on you can go with:
if (found) {
// do something
} else {
// do something else
}
Unless you want to restructure it like this:
vendors = {
Magenic: {
Name: 'Magenic',
ID: 'ABC'
},
Microsoft: {
Name: 'Microsoft',
ID: 'DEF'
} and so on...
};
to which you can do if(vendors.Magnetic)
You will have to loop
May be too late, but javascript array has two methods some and every method that returns a boolean and can help you achieve this.
I think some would be most appropriate for what you intend to achieve.
vendors.some( vendor => vendor['Name'] !== 'Magenic' )
Some validates that any of the objects in the array satisfies the given condition.
vendors.every( vendor => vendor['Name'] !== 'Magenic' )
Every validates that all the objects in the array satisfies the given condition.
As per ECMAScript 6 specification, you can use findIndex.
const magenicIndex = vendors.findIndex(vendor => vendor.Name === 'Magenic');
magenicIndex will hold either 0 (which is the index in the array) or -1 if it wasn't found.
As the OP has asked the question if the key exists or not.
A more elegant solution that will return boolean using ES6 reduce function can be
const magenicVendorExists = vendors.reduce((accumulator, vendor) => (accumulator||vendor.Name === "Magenic"), false);
Note: The initial parameter of reduce is a false and if the array has the key it will return true.
Hope it helps for better and cleaner code implementation
You cannot without looking into the object really.
You probably should change your structure a little, like
vendors = {
Magenic: 'ABC',
Microsoft: 'DEF'
};
Then you can just use it like a lookup-hash.
vendors['Microsoft']; // 'DEF'
vendors['Apple']; // undefined
const check = vendors.find((item)=>item.Name==='Magenic')
console.log(check)
Try this code.
If the item or element is present then the output will show you that element. If it is not present then the output will be 'undefined'.
Testing for array elements:
JS Offers array functions which allow you to achieve this relatively easily. They are the following:
Array.prototype.filter: Takes a callback function which is a test, the array is then iterated over with is callback and filtered according to this callback. A new filtered array is returned.
Array.prototype.some: Takes a callback function which is a test, the array is then iterated over with is callback and if any element passes the test, the boolean true is returned. Otherwise false is returned
The specifics are best explained via an example:
Example:
vendors = [
{
Name: 'Magenic',
ID: 'ABC'
},
{
Name: 'Microsoft',
ID: 'DEF'
} //and so on goes array...
];
// filter returns a new array, we instantly check if the length
// is longer than zero of this newly created array
if (vendors.filter(company => company.Name === 'Magenic').length ) {
console.log('I contain Magenic');
}
// some would be a better option then filter since it directly returns a boolean
if (vendors.some(company => company.Name === 'Magenic')) {
console.log('I also contain Magenic');
}
Browser support:
These 2 function are ES6 function, not all browsers might support them. To overcome this you can use a polyfill. Here is the polyfill for Array.prototype.some (from MDN):
if (!Array.prototype.some) {
Array.prototype.some = function(fun, thisArg) {
'use strict';
if (this == null) {
throw new TypeError('Array.prototype.some called on null or undefined');
}
if (typeof fun !== 'function') {
throw new TypeError();
}
var t = Object(this);
var len = t.length >>> 0;
for (var i = 0; i < len; i++) {
if (i in t && fun.call(thisArg, t[i], i, t)) {
return true;
}
}
return false;
};
}
Simplest method so far:
if (vendors.findIndex(item => item.Name == "Magenic") == -1) {
//not found item
} else {
//found item
}
My approach to solving this problem is to use ES6 and creating a function that does the check for us. The benefit of this function is that it can be reusable through out your project to check any array of objects given the key and the value to check.
ENOUGH TALK, LET'S SEE THE CODE
Array
const ceos = [
{
name: "Jeff Bezos",
company: "Amazon"
},
{
name: "Mark Zuckerberg",
company: "Facebook"
},
{
name: "Tim Cook",
company: "Apple"
}
];
Function
const arrayIncludesInObj = (arr, key, valueToCheck) => {
return arr.some(value => value[key] === valueToCheck);
}
Call/Usage
const found = arrayIncludesInObj(ceos, "name", "Tim Cook"); // true
const found = arrayIncludesInObj(ceos, "name", "Tim Bezos"); // false
2021 Solution*
Lodash .some (docs) is a clean solution, if you use the _matchesProperty (docs) shorthand:
_.some(VENDORS, ['Name', 'Magenic'])
Explanation
This will iterate through the VENDORS Array looking for an element Object with the Name key having a value of the String 'Magenic'. Once it finds this element, it returns true and stops iterating. If it doesn't find the element after looking through the entire Array, it returns false.
Code snippet
const VENDORS = [{ Name: 'Magenic', ID: 'ABC' }, { Name: 'Microsoft', ID: 'DEF' }];
console.log(_.some(VENDORS, ['Name', 'Magenic'])); // true
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.20/lodash.min.js"></script>
* Note that this uses the popular lodash library to achieve the simplest/shortest possible solution. I'm offering this as an alternative to the existing vanilla JS solutions, for those who are interested.
You have to loop, there is no way around it.
function seekVendor(vendors, name) {
for (var i=0, l=vendors.length; i<l; i++) {
if (typeof vendors[i] == "object" && vendors[i].Name === name) {
return vendors[i];
}
}
}
Of course you could use a library like linq.js to make this more pleasing:
Enumerable.From(vendors).Where("$.Name == 'Magenic'").First();
(see jsFiddle for a demo)
I doubt that linq.js will be faster than a straight-forward loop, but it certainly is more flexible when things get a little more complicated.
Correct me if i'm wrong..
i could have used forEach method like this,
var found=false;
vendors.forEach(function(item){
if(item.name === "name"){
found=true;
}
});
Nowadays i'm used to it ,because of it simplicity and self explanatory word.
Thank you.
Functions map, filter, find, and similar are slower than the simple loop.
For me they also less readable than the simple loop and harder to debug. Using them looks like a kind of irrational ritual.
Better have something like this:
arrayHelper = {
arrayContainsObject: function (array, object, key){
for (let i = 0; i < array.length; i++){
if (object[key] === array[i][key]){
return true;
}
}
return false;
}
};
And use it like this with given OP example:
vendors = [{
Name: 'Magenic',
ID: 'ABC'
},
{
Name: 'Microsoft',
ID: 'DEF'
}
];
let abcObject = {ID: 'ABC', Name: 'Magenic'};
let isContainObject = arrayHelper.arrayContainsObject(vendors, abcObject, 'ID');
if you're using jquery you can take advantage of grep to create array with all matching objects:
var results = $.grep(vendors, function (e) {
return e.Name == "Magenic";
});
and then use the results array:
for (var i=0, l=results.length; i<l; i++) {
console.log(results[i].ID);
}
You can use lodash. If lodash library is too heavy for your application consider chunking out unnecessary function not used.
let newArray = filter(_this.props.ArrayOne, function(item) {
return find(_this.props.ArrayTwo, {"speciesId": item.speciesId});
});
This is just one way to do this. Another one can be:
var newArray= [];
_.filter(ArrayOne, function(item) {
return AllSpecies.forEach(function(cItem){
if (cItem.speciesId == item.speciesId){
newArray.push(item);
}
})
});
console.log(arr);
The above example can also be rewritten without using any libraries like:
var newArray= [];
ArrayOne.filter(function(item) {
return ArrayTwo.forEach(function(cItem){
if (cItem.speciesId == item.speciesId){
newArray.push(item);
}
})
});
console.log(arr);
Hope my answer helps.
Many answers here are good and pretty easy. But if your array of object is having a fixed set of value then you can use below trick:
Map all the name in a object.
vendors = [
{
Name: 'Magenic',
ID: 'ABC'
},
{
Name: 'Microsoft',
ID: 'DEF'
}
];
var dirtyObj = {}
for(var count=0;count<vendors.length;count++){
dirtyObj[vendors[count].Name] = true //or assign which gives you true.
}
Now this dirtyObj you can use again and again without any loop.
if(dirtyObj[vendor.Name]){
console.log("Hey! I am available.");
}
To compare one object to another, I combine a for in loop (used to loop through objects) and some().
You do not have to worry about an array going out of bounds etc, so that saves some code. Documentation on .some can be found here
var productList = [{id: 'text3'}, {id: 'text2'}, {id: 'text4', product: 'Shampoo'}]; // Example of selected products
var theDatabaseList = [{id: 'text1'}, {id: 'text2'},{id: 'text3'},{id:'text4', product: 'shampoo'}];
var objectsFound = [];
for(let objectNumber in productList){
var currentId = productList[objectNumber].id;
if (theDatabaseList.some(obj => obj.id === currentId)) {
// Do what you need to do with the matching value here
objectsFound.push(currentId);
}
}
console.log(objectsFound);
An alternative way I compare one object to another is to use a nested for loop with Object.keys().length to get the amount of objects in the array. Code below:
var productList = [{id: 'text3'}, {id: 'text2'}, {id: 'text4', product: 'Shampoo'}]; // Example of selected products
var theDatabaseList = [{id: 'text1'}, {id: 'text2'},{id: 'text3'},{id:'text4', product: 'shampoo'}];
var objectsFound = [];
for(var i = 0; i < Object.keys(productList).length; i++){
for(var j = 0; j < Object.keys(theDatabaseList).length; j++){
if(productList[i].id === theDatabaseList[j].id){
objectsFound.push(productList[i].id);
}
}
}
console.log(objectsFound);
To answer your exact question, if are just searching for a value in an object, you can use a single for in loop.
var vendors = [
{
Name: 'Magenic',
ID: 'ABC'
},
{
Name: 'Microsoft',
ID: 'DEF'
}
];
for(var ojectNumbers in vendors){
if(vendors[ojectNumbers].Name === 'Magenic'){
console.log('object contains Magenic');
}
}
Alternatively you can do:
const find = (key, needle) => return !!~vendors.findIndex(v => (v[key] === needle));
var without2 = (arr, args) => arr.filter(v => v.id !== args.id);
Example:
without2([{id:1},{id:1},{id:2}],{id:2})
Result:
without2([{id:1},{id:1},{id:2}],{id:2})
You can try this its work for me.
const _ = require('lodash');
var arr = [
{
name: 'Jack',
id: 1
},
{
name: 'Gabriel',
id: 2
},
{
name: 'John',
id: 3
}
]
function findValue(arr,value) {
return _.filter(arr, function (object) {
return object['name'].toLowerCase().indexOf(value.toLowerCase()) >= 0;
});
}
console.log(findValue(arr,'jack'))
//[ { name: 'Jack', id: 1 } ]
const a = [{one:2},{two:2},{two:4}]
const b = a.filter(val => "two" in val).length;
if (b) {
...
}
I would rather go with regex.
If your code is as follows,
vendors = [
{
Name: 'Magenic',
ID: 'ABC'
},
{
Name: 'Microsoft',
ID: 'DEF'
}
];
I would recommend
/"Name":"Magenic"/.test(JSON.stringify(vendors))

Is there a fast way to select item from array of objects in Javascript?

I have an array:
[
{
key: 'some key',
value: 'some value'
}, {
...
}, ...
]
Is there any simple way to get a certain element of this array without iterating through it and comparing each actual key with desired one?
I currently have a function
var select = function(what, from) {
for (var i in from) {
if (from[i].key == what) {
return from[i];
}
}
return null;
};
I believe there's a better way to handle it.
The short answer is no. There are lots of options to make your code cleaner - like all of the above - but you are still going to have to iterate through them one by one.
You can do 2 things to make it better:
Convert to Map
If you are going to do the search more than once or twice, then convert it to a map of the keys, so at least each subsequent lookup is O(1) instead of O(n). A number of answers suggested that - I use it a lot in my own code - but here is a basic version (there is a shorthand un underscore/lodash):
var i, hash = {};
for (i = 0; i < arr.length; i++) {
hash[arr[i].key] = arr[i].value;
}
Then for all future lookups it is a simple:
var value = hash[key];
Search Algorithms
If it needs to stay as an array for whatever reason, and you have some knowledge about what that array's values will be like, you can use all sorts of search algorithms. Cycling through them one by one is good for an even distribution, but will still be O(n), which will, on average require cycling through half the array (n/2) each time.
But search algorithms are beyond this post...
You were close. This would be the fastest form in this particular case:
var select = function(key, expected, from) {
var i;
for (i = 0; i < from.length; i += 1) {
if (from[i][key] === expected) {
return from[i];
}
}
return null;
};
select('thing', 'foo', [{thing: 'foo'}]);
// => {thing: 'foo'}
But do you require maximum speed? Or would a more elegant, general-purpose solution suit you? If so, use a predicate:
var find = function (array, predicate) {
var i;
for (i = 0; i < array.length; i += 1) {
if (predicate(array[i])) {
return array[i];
}
}
return null;
};
// Find, in an array, an item which passes the following truth test.
find([{thing: 'foo'}], function (item) {
return item.thing === 'foo';
});
// => {thing: 'foo'}
If you don't like writing boilerplate utility code like this, I recommend leveraging a library like lodash. It has a find method already and it's faster and more powerful than anything we could come up with.
Unfortunately not, however, if you alter your structure to use properties it will be much faster. Any other approach will require iteration over the array and will therefore always be less efficient than the below.
var keyValueObj = {
someKey: 'someValue',
nextKey: 'nextValue'
}
// represents 'someValue'
keyValueObj['someKey']
If you're using modern browsers, this should do the magic:
function include(arr,obj) {
return (arr.indexOf(obj) != -1);
}
or in jquery
$.inArray(value, array)
In response to your comment, check this link, this will also address your problem in the comment.
If your keys are strings use them as object properties instead of an array of objects
If your keys are objects use Map()
You can refactor your array into a map, which is, by design, good for quick and effective lookup:
{
'some key': {
value: 'some value'
},
'some other key': {
value: 'some other value'
},
}
Then your select function would be as short as
var select = function(what, from) {
return from[what];
};
If you expect return value of the select() to still have the .key property (which is redundant, as the caller already knows it as what), it is easy to achieve by either keeping the key property in map elements, or auto-adding in in select() before returning.
If you need to run this function very often, I would recommend creating an index:
var yourItemList = [{...}],
select = (function (items) {
var i = 0,
l = items.length,
preparedItems = {};
for (; i < l; ++i) {
preparedItems[items[i].key] = items[i].value;
}
return function (key) {
return preparedItems[key];
};
}(yourItemList));
This way you only iterate once on the item list. After that every select('theKeyYouNeed') just uses the mapped "index".
You can filter array. It is build in Array method so you don't need to declare other helper functions
var arr = [
{
key: 'some key',
value: 'some value'
}, {
key: 'some key1',
value: 'some value'
}
];
arr = arr.filter(function(item) {
return item.key === 'some key'
});
Now arr has only first object (because its key equal to 'some key'). Object that you find is arr[0].
You can use find() instead of filter() when you need just one object. Note, that find() is part of ES6 and has no support in all major browsers

how to loop through an array with nested objects using jquery?

if I have an array like this:
var msg = [ {name: ["a1", "a2"], value: "this is A"},
{name: ["b1", "b2"], value: "this is B"},
...
]
The array contains global errors messages for client-side form validations. I have managed to pass in faulty inputs (e.g. "a1") and now am wondering how to get the corresponding message out of my ill-constructed array.
Question
What would be the best way to loop through this array? for example, if I have "a1" as parameter passed into my function, how do I extract "this is A" as corresponding message?
inArray doesn't really help, because I need the corresponding message and not the position of a1. I'm also not sure if this is the best way to store my error messages... ideas welcome!
Thanks for help!
Re-arrange your data structure:
var my_param = 'b1';
// This is an object, so we can have key/value pairs
var error_codes =
{
'a1': 0,
'a2': 0,
'b1': 1,
'b2': 1
};
// This is an array because we only need values
var error_messages =
[
'This is A',
'This is b'
];
alert(error_messages[error_codes[my_param]]);
This makes it really easy to set up new error codes and messages, and is extremely easy to understand. The only gotcha is error_codes[my_param] - it's an object, but we can't do error_codes.my_param because it'll look for the element called 'my_param', so using array notation, we can look up the object key.
The only other potential trap is making sure you don't have any trailing commas:
var error_codes = { 'a1': 1, }; // NO!
Also knows as the trailing comma of death!
This would be how I'd do it
var myMsg = findMsg('a1')
function findMsg(msgType){
msg.forEach(function(obj){
if(inArray(msgType, obj.name) !== -1){
return obj.value
}
})
}
function inArray(key, obj){
return obj.join().indexOf(key)
}
$.each is the jQuery way for taking action on each element of an array or each enumerable property of an object.
var value;
$.each(msg, function (i, el) {
if (el.name.indexOf(name) >= 0) {
value = el.value;
return false; // Stops iteration.
}
});
If name is "a1" then after running the above, value === "this is A".
Nice and simple:
var getMessage = function (name)
{
var msg = [ ... ];
for(var i = 0; i < msg.length; ++ i)
if (msg [i].name.indexOf (name) != -1)
return msg [i].value;
}
Returns either the corresponding message or undefined if the name wasn't found.
You may need a shim for indexOf depending on which browsers you want to support:
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf

How to determine if Javascript array contains an object with an attribute that equals a given value?

I have an array like
vendors = [{
Name: 'Magenic',
ID: 'ABC'
},
{
Name: 'Microsoft',
ID: 'DEF'
} // and so on...
];
How do I check this array to see if "Magenic" exists? I don't want to loop, unless I have to. I'm working with potentially a couple thousand records.
No need to reinvent the wheel loop, at least not explicitly (using arrow functions, modern browsers only):
if (vendors.filter(e => e.Name === 'Magenic').length > 0) {
/* vendors contains the element we're looking for */
}
or, better yet, use some as it allows the browser to stop as soon as one element is found that matches, so it's going to be faster:
if (vendors.some(e => e.Name === 'Magenic')) {
/* vendors contains the element we're looking for */
}
or the equivalent (in this case) find:
if (vendors.find(e => e.Name === 'Magenic')) {
/* same result as above, but a different function return type */
}
And you can even get the position of that element by using findIndex:
const i = vendors.findIndex(e => e.Name === 'Magenic');
if (i > -1) {
/* vendors contains the element we're looking for, at index "i" */
}
And if you need compatibility with lousy browsers then your best bet is:
if (vendors.filter(function(e) { return e.Name === 'Magenic'; }).length > 0) {
/* vendors contains the element we're looking for */
}
2018 edit: This answer is from 2011, before browsers had widely supported array filtering methods and arrow functions. Have a look at CAFxX's answer.
There is no "magic" way to check for something in an array without a loop. Even if you use some function, the function itself will use a loop. What you can do is break out of the loop as soon as you find what you're looking for to minimize computational time.
var found = false;
for(var i = 0; i < vendors.length; i++) {
if (vendors[i].Name == 'Magenic') {
found = true;
break;
}
}
No loop necessary. Three methods that come to mind:
Array.prototype.some()
This is the most exact answer for your question, i.e. "check if something exists", implying a bool result. This will be true if there are any 'Magenic' objects, false otherwise:
let hasMagenicVendor = vendors.some( vendor => vendor['Name'] === 'Magenic' )
Array.prototype.filter()
This will return an array of all 'Magenic' objects, even if there is only one (will return a one-element array):
let magenicVendors = vendors.filter( vendor => vendor['Name'] === 'Magenic' )
If you try to coerce this to a boolean, it will not work, as an empty array (no 'Magenic' objects) is still truthy. So just use magenicVendors.length in your conditional.
Array.prototype.find()
This will return the first 'Magenic' object (or undefined if there aren't any):
let magenicVendor = vendors.find( vendor => vendor['Name'] === 'Magenic' );
This coerces to a boolean okay (any object is truthy, undefined is falsy).
Note: I'm using vendor["Name"] instead of vendor.Name because of the weird casing of the property names.
Note 2: No reason to use loose equality (==) instead of strict equality (===) when checking the name.
The accepted answer still works but now we have an ECMAScript 6 native methods [Array.find][1] and [Array.some][2] to achieve the same effect.
Array.some
Use some If you only want to determine if an element exists i.e. you need a true/false determination.
Quoting MDN:
The some() method tests whether at least one element in the array passes the test implemented by the provided function. It returns true if, in the array, it finds an element for which the provided function returns true; otherwise it returns false. It doesn't modify the array.
Array.find
Use find if you want to get the matched object from array else returns undefined.
Quoting MDN:
The find() method returns the value of the first element in the provided array that satisfies the provided testing function. If no values satisfy the testing function, undefined is returned.
var arr = [
{
id: 21,
label: 'Banana',
},
{
id: 22,
label: 'Apple',
}
]
/* note : data is the actual object that matched search criteria
or undefined if nothing matched */
var data = arr.find(function(ele) {
return ele.id === 21;
});
if (data) {
console.log('found');
console.log(data); // This is entire object i.e. `item` not boolean
}
/* note : doesExist is a boolean thats true or false depending on of whether the data was found or not */
var doesExist = arr.some(function(ele) {
return ele.id === 21;
});
See my jsfiddle link There is a polyfill for IE provided by mozilla
Here's the way I'd do it
const found = vendors.some(item => item.Name === 'Magenic');
array.some() method checks if there is at least one value in an array that matches criteria and returns a boolean.
From here on you can go with:
if (found) {
// do something
} else {
// do something else
}
Unless you want to restructure it like this:
vendors = {
Magenic: {
Name: 'Magenic',
ID: 'ABC'
},
Microsoft: {
Name: 'Microsoft',
ID: 'DEF'
} and so on...
};
to which you can do if(vendors.Magnetic)
You will have to loop
May be too late, but javascript array has two methods some and every method that returns a boolean and can help you achieve this.
I think some would be most appropriate for what you intend to achieve.
vendors.some( vendor => vendor['Name'] !== 'Magenic' )
Some validates that any of the objects in the array satisfies the given condition.
vendors.every( vendor => vendor['Name'] !== 'Magenic' )
Every validates that all the objects in the array satisfies the given condition.
As per ECMAScript 6 specification, you can use findIndex.
const magenicIndex = vendors.findIndex(vendor => vendor.Name === 'Magenic');
magenicIndex will hold either 0 (which is the index in the array) or -1 if it wasn't found.
As the OP has asked the question if the key exists or not.
A more elegant solution that will return boolean using ES6 reduce function can be
const magenicVendorExists = vendors.reduce((accumulator, vendor) => (accumulator||vendor.Name === "Magenic"), false);
Note: The initial parameter of reduce is a false and if the array has the key it will return true.
Hope it helps for better and cleaner code implementation
You cannot without looking into the object really.
You probably should change your structure a little, like
vendors = {
Magenic: 'ABC',
Microsoft: 'DEF'
};
Then you can just use it like a lookup-hash.
vendors['Microsoft']; // 'DEF'
vendors['Apple']; // undefined
const check = vendors.find((item)=>item.Name==='Magenic')
console.log(check)
Try this code.
If the item or element is present then the output will show you that element. If it is not present then the output will be 'undefined'.
Testing for array elements:
JS Offers array functions which allow you to achieve this relatively easily. They are the following:
Array.prototype.filter: Takes a callback function which is a test, the array is then iterated over with is callback and filtered according to this callback. A new filtered array is returned.
Array.prototype.some: Takes a callback function which is a test, the array is then iterated over with is callback and if any element passes the test, the boolean true is returned. Otherwise false is returned
The specifics are best explained via an example:
Example:
vendors = [
{
Name: 'Magenic',
ID: 'ABC'
},
{
Name: 'Microsoft',
ID: 'DEF'
} //and so on goes array...
];
// filter returns a new array, we instantly check if the length
// is longer than zero of this newly created array
if (vendors.filter(company => company.Name === 'Magenic').length ) {
console.log('I contain Magenic');
}
// some would be a better option then filter since it directly returns a boolean
if (vendors.some(company => company.Name === 'Magenic')) {
console.log('I also contain Magenic');
}
Browser support:
These 2 function are ES6 function, not all browsers might support them. To overcome this you can use a polyfill. Here is the polyfill for Array.prototype.some (from MDN):
if (!Array.prototype.some) {
Array.prototype.some = function(fun, thisArg) {
'use strict';
if (this == null) {
throw new TypeError('Array.prototype.some called on null or undefined');
}
if (typeof fun !== 'function') {
throw new TypeError();
}
var t = Object(this);
var len = t.length >>> 0;
for (var i = 0; i < len; i++) {
if (i in t && fun.call(thisArg, t[i], i, t)) {
return true;
}
}
return false;
};
}
Simplest method so far:
if (vendors.findIndex(item => item.Name == "Magenic") == -1) {
//not found item
} else {
//found item
}
My approach to solving this problem is to use ES6 and creating a function that does the check for us. The benefit of this function is that it can be reusable through out your project to check any array of objects given the key and the value to check.
ENOUGH TALK, LET'S SEE THE CODE
Array
const ceos = [
{
name: "Jeff Bezos",
company: "Amazon"
},
{
name: "Mark Zuckerberg",
company: "Facebook"
},
{
name: "Tim Cook",
company: "Apple"
}
];
Function
const arrayIncludesInObj = (arr, key, valueToCheck) => {
return arr.some(value => value[key] === valueToCheck);
}
Call/Usage
const found = arrayIncludesInObj(ceos, "name", "Tim Cook"); // true
const found = arrayIncludesInObj(ceos, "name", "Tim Bezos"); // false
2021 Solution*
Lodash .some (docs) is a clean solution, if you use the _matchesProperty (docs) shorthand:
_.some(VENDORS, ['Name', 'Magenic'])
Explanation
This will iterate through the VENDORS Array looking for an element Object with the Name key having a value of the String 'Magenic'. Once it finds this element, it returns true and stops iterating. If it doesn't find the element after looking through the entire Array, it returns false.
Code snippet
const VENDORS = [{ Name: 'Magenic', ID: 'ABC' }, { Name: 'Microsoft', ID: 'DEF' }];
console.log(_.some(VENDORS, ['Name', 'Magenic'])); // true
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.20/lodash.min.js"></script>
* Note that this uses the popular lodash library to achieve the simplest/shortest possible solution. I'm offering this as an alternative to the existing vanilla JS solutions, for those who are interested.
You have to loop, there is no way around it.
function seekVendor(vendors, name) {
for (var i=0, l=vendors.length; i<l; i++) {
if (typeof vendors[i] == "object" && vendors[i].Name === name) {
return vendors[i];
}
}
}
Of course you could use a library like linq.js to make this more pleasing:
Enumerable.From(vendors).Where("$.Name == 'Magenic'").First();
(see jsFiddle for a demo)
I doubt that linq.js will be faster than a straight-forward loop, but it certainly is more flexible when things get a little more complicated.
Correct me if i'm wrong..
i could have used forEach method like this,
var found=false;
vendors.forEach(function(item){
if(item.name === "name"){
found=true;
}
});
Nowadays i'm used to it ,because of it simplicity and self explanatory word.
Thank you.
Functions map, filter, find, and similar are slower than the simple loop.
For me they also less readable than the simple loop and harder to debug. Using them looks like a kind of irrational ritual.
Better have something like this:
arrayHelper = {
arrayContainsObject: function (array, object, key){
for (let i = 0; i < array.length; i++){
if (object[key] === array[i][key]){
return true;
}
}
return false;
}
};
And use it like this with given OP example:
vendors = [{
Name: 'Magenic',
ID: 'ABC'
},
{
Name: 'Microsoft',
ID: 'DEF'
}
];
let abcObject = {ID: 'ABC', Name: 'Magenic'};
let isContainObject = arrayHelper.arrayContainsObject(vendors, abcObject, 'ID');
if you're using jquery you can take advantage of grep to create array with all matching objects:
var results = $.grep(vendors, function (e) {
return e.Name == "Magenic";
});
and then use the results array:
for (var i=0, l=results.length; i<l; i++) {
console.log(results[i].ID);
}
You can use lodash. If lodash library is too heavy for your application consider chunking out unnecessary function not used.
let newArray = filter(_this.props.ArrayOne, function(item) {
return find(_this.props.ArrayTwo, {"speciesId": item.speciesId});
});
This is just one way to do this. Another one can be:
var newArray= [];
_.filter(ArrayOne, function(item) {
return AllSpecies.forEach(function(cItem){
if (cItem.speciesId == item.speciesId){
newArray.push(item);
}
})
});
console.log(arr);
The above example can also be rewritten without using any libraries like:
var newArray= [];
ArrayOne.filter(function(item) {
return ArrayTwo.forEach(function(cItem){
if (cItem.speciesId == item.speciesId){
newArray.push(item);
}
})
});
console.log(arr);
Hope my answer helps.
Many answers here are good and pretty easy. But if your array of object is having a fixed set of value then you can use below trick:
Map all the name in a object.
vendors = [
{
Name: 'Magenic',
ID: 'ABC'
},
{
Name: 'Microsoft',
ID: 'DEF'
}
];
var dirtyObj = {}
for(var count=0;count<vendors.length;count++){
dirtyObj[vendors[count].Name] = true //or assign which gives you true.
}
Now this dirtyObj you can use again and again without any loop.
if(dirtyObj[vendor.Name]){
console.log("Hey! I am available.");
}
To compare one object to another, I combine a for in loop (used to loop through objects) and some().
You do not have to worry about an array going out of bounds etc, so that saves some code. Documentation on .some can be found here
var productList = [{id: 'text3'}, {id: 'text2'}, {id: 'text4', product: 'Shampoo'}]; // Example of selected products
var theDatabaseList = [{id: 'text1'}, {id: 'text2'},{id: 'text3'},{id:'text4', product: 'shampoo'}];
var objectsFound = [];
for(let objectNumber in productList){
var currentId = productList[objectNumber].id;
if (theDatabaseList.some(obj => obj.id === currentId)) {
// Do what you need to do with the matching value here
objectsFound.push(currentId);
}
}
console.log(objectsFound);
An alternative way I compare one object to another is to use a nested for loop with Object.keys().length to get the amount of objects in the array. Code below:
var productList = [{id: 'text3'}, {id: 'text2'}, {id: 'text4', product: 'Shampoo'}]; // Example of selected products
var theDatabaseList = [{id: 'text1'}, {id: 'text2'},{id: 'text3'},{id:'text4', product: 'shampoo'}];
var objectsFound = [];
for(var i = 0; i < Object.keys(productList).length; i++){
for(var j = 0; j < Object.keys(theDatabaseList).length; j++){
if(productList[i].id === theDatabaseList[j].id){
objectsFound.push(productList[i].id);
}
}
}
console.log(objectsFound);
To answer your exact question, if are just searching for a value in an object, you can use a single for in loop.
var vendors = [
{
Name: 'Magenic',
ID: 'ABC'
},
{
Name: 'Microsoft',
ID: 'DEF'
}
];
for(var ojectNumbers in vendors){
if(vendors[ojectNumbers].Name === 'Magenic'){
console.log('object contains Magenic');
}
}
Alternatively you can do:
const find = (key, needle) => return !!~vendors.findIndex(v => (v[key] === needle));
var without2 = (arr, args) => arr.filter(v => v.id !== args.id);
Example:
without2([{id:1},{id:1},{id:2}],{id:2})
Result:
without2([{id:1},{id:1},{id:2}],{id:2})
You can try this its work for me.
const _ = require('lodash');
var arr = [
{
name: 'Jack',
id: 1
},
{
name: 'Gabriel',
id: 2
},
{
name: 'John',
id: 3
}
]
function findValue(arr,value) {
return _.filter(arr, function (object) {
return object['name'].toLowerCase().indexOf(value.toLowerCase()) >= 0;
});
}
console.log(findValue(arr,'jack'))
//[ { name: 'Jack', id: 1 } ]
const a = [{one:2},{two:2},{two:4}]
const b = a.filter(val => "two" in val).length;
if (b) {
...
}
I would rather go with regex.
If your code is as follows,
vendors = [
{
Name: 'Magenic',
ID: 'ABC'
},
{
Name: 'Microsoft',
ID: 'DEF'
}
];
I would recommend
/"Name":"Magenic"/.test(JSON.stringify(vendors))

Is array both associative and indexed?

Can an array in JavaScript be associative AND indexed?
I'd like to be able to lookup an item in the array by its position or a key value.
There are no such things as associative arrays in Javascript. You can use object literals, which look like associative arrays, but they have unordered properties. Regular Javascript arrays are based on integer indexes, and can't be associative.
For example, with this object:
var params = {
foo: 1,
bar: 0,
other: 2
};
You can access properties from the object, for example:
params["foo"];
And you can also iterate over the object using the for...in statement:
for(var v in params) {
//v is equal to the currently iterated property
}
However, there is no strict rule on the order of property iteration - two iterations of your object literal could return the properties in different orders.
After reading the Wikipedia definition of associative array, I'm going to break with traditional JavaScript lore and say, "yes, JavaScript does have associative arrays." With JavaScript arrays, you can add, reassign, remove, and lookup values by their keys (and the keys can be quoted strings), which is what Wikipedia says associative arrays should be able to do.
However, you seem to be asking something different--whether you can look up the same value by either index or key. That's not a requirement of associative arrays (see the Wikipedia article.) Associative arrays don't have to give you the ability to get a value by index.
JavaScript arrays are very closely akin to JavaScript objects.
arr=[];
arr[0]="zero";
arr[1]="one";
arr[2]="two";
arr["fancy"]="what?";
Yes, that's an array, and yes, you can get away with non-numeric indices. (If you're curious, after all this, arr.length is 3.)
In most cases, I think you should stick to numeric indices when you use arrays. That what most programmers expect, I think.
The link is to my blog post about the subject.
Native JS objects only accept strings as property names, which is true even for numeric array indices; arrays differ from vanilla objects only insofar as most JS implementations will store numerically indexed properties differently (ie in an actual array as long as they are dense) and setting them will trigger additional operations (eg adjustment of the length property).
If you're looking for a map which accepts arbitrary keys, you'll have to use a non-native implementation. The script is intended for fast iteration and not random-access by numeric indices, so it might nor be what you're looking for.
A barebones implementation of a map which would do what you're asking for could look like this:
function Map() {
this.length = 0;
this.store = {};
}
Map.prototype.get = function(key) {
return this.store.hasOwnProperty(key) ?
this.store[key] : undefined;
};
Map.prototype.put = function(key, value, index) {
if(arguments.length < 3) {
if(this.store.hasOwnProperty(key)) {
this.store[key].value = value;
return this;
}
index = this.length;
}
else if(index >>> 0 !== index || index >= 0xffffffff)
throw new Error('illegal index argument');
if(index >= this.length)
this.length = index + 1;
this[index] = this.store[key] =
{ index : index, key : key, value : value };
return this;
};
The index argument of put() is optional.
You can access the values in a map map either by key or index via
map.get('key').value
map[2].value
var myArray = Array();
myArray["first"] = "Object1";
myArray["second"] = "Object2";
myArray["third"] = "Object3";
Object.keys(myArray); // returns ["first", "second", "third"]
Object.keys(myArray).length; // returns 3
if you want the first element then you can use it like so:
myArray[Object.keys(myArray)[0]]; // returns "Object1"
The order in which objects appear in an associative javascript array is not defined, and will differ across different implementations. For that reason you can't really count on a given associative key to always be at the same index.
EDIT:
as Perspx points out, there aren't really true associative arrays in javascript. The statement foo["bar"] is just syntactic sugar for foo.bar
If you trust the browser to maintain the order of elements in an object, you could write a function
function valueForIndex(obj, index) {
var i = 0;
for (var key in obj) {
if (i++ == index)
return obj[key];
}
}
var stuff = [];
stuff[0] = "foo";
stuff.bar = stuff[0]; // stuff.bar can be stuff["bar"] if you prefer
var key = "bar";
alert(stuff[0] + ", " + stuff[key]); // shows "foo, foo"
I came here to wanting to know if this is bad practice or not, and instead found a lot of people appearing not to understand the question.
I wanted to have a data structure that was ordered but could be indexed by key, so that it wouldn't require iteration for every lookup.
In practical terms this is quite simple, but I still haven't read anything on whether it's a terrible practice or not.
var roygbiv = [];
var colour = { key : "red", hex : "#FF0000" };
roygbiv.push(colour);
roygbiv[colour.key] = colour;
...
console.log("Hex colours of the rainbow in order:");
for (var i = 0; i < roygbiv.length; i++) {
console.log(roygbiv[i].key + " is " + roygbiv[i].hex);
}
// input = "red";
console.log("Hex code of input colour:");
console.log(roygbiv[input].hex);
The important thing is to never change the value of array[index] or array[key] directly once the object is set up or the values will no longer match. If the array contains objects you can change the properties of those objects and you will be able to access the changed properties by either method.
Although I agree with the answers given you can actually accomplish what you are saying with getters and setters. For example:
var a = [1];
//This makes a["blah"] refer to a[0]
a.__defineGetter__("blah", function(){return this[0]});
//This makes a["blah"] = 5 actually store 5 into a[0]
a.__defineSetter__("blah", function(val){ this[0] = val});
alert(a["blah"]); // emits 1
a["blah"] = 5;
alert(a[0]); // emits 5
Is this what you are looking for? i think theres a different more modern way to do getters and setters but cant remember.
The tide has changed on this one. Now you can do that... and MORE! Using Harmony Proxies you could definitely solve this problem in many ways.
You'll have to verify that your targeted environments support this with maybe a little help from the harmony-reflect shim.
There's a really good example on the Mozilla Developer Network on using a Proxy to find an array item object by it's property which pretty much sums it up.
Here's my version:
var players = new Proxy(
[{
name: 'monkey',
score: 50
}, {
name: 'giraffe',
score: 100
}, {
name: 'pelican',
score: 150
}], {
get: function(obj, prop) {
if (prop in obj) {
// default behavior
return obj[prop];
}
if (typeof prop == 'string') {
if (prop == 'rank') {
return obj.sort(function(a, b) {
return a.score > b.score ? -1 : 1;
});
}
if (prop == 'revrank') {
return obj.sort(function(a, b) {
return a.score < b.score ? -1 : 1;
});
}
var winner;
var score = 0;
for (var i = 0; i < obj.length; i++) {
var player = obj[i];
if (player.name == prop) {
return player;
} else if (player.score > score) {
score = player.score;
winner = player;
}
}
if (prop == 'winner') {
return winner;
}
return;
}
}
});
console.log(players[0]); // { name: 'monkey', score: 50 }
console.log(players['monkey']); // { name: 'monkey', score: 50 }
console.log(players['zebra']); // undefined
console.log(players.rank); // [ { name: 'pelican', score: 150 },{ name: 'giraffe', score: 100 }, { name: 'monkey', score: 50 } ]
console.log(players.revrank); // [ { name: 'monkey', score: 50 },{ name: 'giraffe', score: 100 },{ name: 'pelican', score: 150 } ]
console.log(players.winner); // { name: 'pelican', score: 150 }
The latest MDN documentation makes it quiet clear that Array index must be integers.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
let arr=[];
arr[0]="zero";
arr[1]="one";
arr[2]="two";
arr["fancy"]="what?";
//Arrays cannot use strings as element indexes (as in an associative array) but must use integers.
//Setting non-integers using bracket notation will not set an element to the Array List itself
//A non-integer will set a variable associated with that ARRAY Object property collection
let denseKeys = [...arr.keys()];
console.log(denseKeys);//[ 0, 1, 2 ]
console.log("ARRAY Keys:"+denseKeys.length);//3
let sparseKeys = Object.keys(arr);
console.log(sparseKeys);//[ '0', '1', '2', 'fancy' ]
console.log("Object Keys:"+sparseKeys.length);//4
const iterator = arr.keys();
for (const key of iterator) {
console.log(key);//0,1,2
}
Yes.
test = new Array();
test[0] = 'yellow';
test['banana'] = 0;
alert(test[test['banana']]);

Categories