I am trying to check if an array of objects includes a object. I want it to return true when there is a object in the array that has the same values and the object id should not matter. This is how i thought it would work:
let arr = [{r:0, g:1}];
let obj = {r:0, g:1}
console.log(arr.includes(obj));
But it returns false and I need it to return true. Do I have to convert every object in the array to a string with JSON.stringify() and the object I am searching for like this:
let arr = [JSON.stringify({r: 0, g: 1})]
let obj = {r: 0, g: 1}
console.log(arr.includes(JSON.stringify(obj)));
Is there another easier and more efficient way to do it with more objects?
You get false because objects are compared by a reference to the object, while you got there 2 separate object instances.
Wile JSON.stringify might work, keep in mind that the order of properties is not guaranteed and it may fail if the order is not the same, because you get a different string.
you can check for an id property or compare several properties to match against, if you must you can compare all properties with a loop.
If you have an access to the object's reference, you can use a Map or a Set which allows you to store and check references
const obj = {r:0, g:1};
const obj2 = {r:0, g:1};
const mySet = new Set();
// given the fact that you do have access to the object ref
mySet.add(obj);
const isObjInList = mySet.has(obj);
const isObj2InList = mySet.has(obj2);
console.log('is obj in list - ', isObjInList);
console.log('is obj2 in list - ', isObj2InList);
JSON.stringify doesn't work as expected if you change the order of properties in one of the objects.
You can use .some in combination with isEqual from lodash (or other alternatives). Or you can write it by yourself, but be careful, there are too many edge cases, that's why I recommend using an existing approach. There is no need to reinvent the wheel.
let arr = [JSON.stringify({r: 0, g: 1})]
let obj = {g: 1, r: 0}
console.log(arr.includes(JSON.stringify(obj)));
let arr2 = [{r:0, g:1}];
let obj2 = {g:1, r:0};
console.log(arr2.some(item => _.isEqual(item, obj2)));
console.log(_.some(arr2, item => _.isEqual(item, obj2))); // more canonical way
<script src="https://cdn.jsdelivr.net/lodash/4/lodash.min.js"></script>
I like to use Set() for this purposes, read from the documentation:
The Set object lets you store unique values of any type, whether primitive values or object references.
See the below example:
let obj = {r:0, g:1};
const set = new Set();
set.add(obj);
console.log(set.has(obj));
I hope that helps!
You can use the JavaScript some() method to find out if a JavaScript array contains an object.
This method tests whether at least one element in the array passes the test implemented by the provided function. Here's an example that demonstrates how it works:
// An array of objects
var persons = [{name: "Harry"}, {name: "Alice"}, {name: "Peter"}];
// Find if the array contains an object by comparing the property value
if(persons.some(person => person.name === "Peter")){
alert("Object found inside the array.");
} else{
alert("Object not found.");
}
Note that if try to find the object inside an array using the indexOf() method like persons.indexOf({name: "Harry"}) it will not work (always return -1). Because, two distinct objects are not equal even if they look the same (i.e. have the same properties and values). Likewise, two distinct arrays are not equal even if they have the same values in the same order.
The some() method is supported in all major browsers, such as Chrome, Firefox, IE (9 and above), etc. See the tutorial on JavaScript ES6 Features to learn more about arrow function notation.
Related
I discovered a bug on a project I'm working on that can be replicated by this snippet:
const original = [ { value: 1 } ];
function test() {
const copy = Object.assign([], original);
copy.forEach(obj => obj.value = obj.value + 1);
}
console.log(original[0].value); // -> 1, expected 1
test();
console.log(original[0].value); // -> 2, expected 1
test();
console.log(original[0].value); // -> 3, expected 1
I do not understand why this is the case. In the MDN web docs, the following statements can be found in the deep copy warning section:
For deep cloning, we need to use alternatives, because Object.assign() copies property values.
If the source value is a reference to an object, it only copies the reference value.
How do these notes apply to arrays / in this case? Are array values somehow considered as properties?
Looking back now, the method was probably not intended to work with arrays, so I guess I reap what I sow... but I'd still like to understand what's going on here. The intent was to deep copy the array in order to mutate the objects inside while keeping the original intact.
Are array values somehow considered as properties?
Yes. In JavaScript, arrays are objects (which is why Object.assign works with them), and properties with a special class of names called array indexes (strings defining decimal numbers in standard form with numeric values < 232 - 1) represent the elements of the array. (Naturally, JavaScript engines optimize them into true arrays when they can, but they're defined as objects and performing object operations on them is fully supported.) I found this sufficiently surprising when getting deep into JavaScript that I wrote it up on my anemic old blog.
Given:
const obj = {a: 1};
const arr = [1];
these two operations are the same from a specification viewpoint:
console.log(obj["a"]);
console.log(arr["0"]); // Yes, in quotes
Of course, we don't normally write the quotes when accessing array elements by index, normally we'll just do arr[0], but in theory, the number is converted to a string and then the property is looked up by name — although, again, modern JavaScript engines optimize.
const obj = {a: 1};
const arr = [1];
console.log(obj["a"]);
console.log(arr["0"]); // Yes, in quotes
console.log(arr[0]);
If you need to clone an array and the objects in it, map + property spread is a useful way to do that, but note the objects are only cloned shallowly (which is often sufficient, but not always):
const result = original.map((value) => ({...value}));
For a full deep copy, see this question's answers.
Here we can use structuredClone for deep copy.
Although counterintuitive, it is possible to create a JavaScript array "property" using dot notation:
const arr = [];
arr.dot = "notation";
console.log(arr.dot); // "notation"
Weird, but ok. However, the array's length still registers as 0:
const arr = [];
arr.dot = "notation";
console.log(arr.dot); // "notation"
console.log(arr.length); // 0
Two questions:
Why is the array's length not impacted by the property assigned via dot notation?
Why is it possible to assign a property to an array via dot notation?
A JavaScript array is just an object. You're setting the property dot on the object.
You can confirm that an array is an object by doing:
typeof arr.
The length property is computed based on the number of numeric entries in the array.
Here's an excerpt taken from developer.mozilla.org:
Arrays cannot use strings as element indexes (as in an associative array) but must use integers. Setting or accessing via non-integers using bracket notation (or dot notation) will not set or retrieve an element from the array list itself, but will set or access a variable associated with that array's object property collection. The array's object properties and list of array elements are separate, and the array's traversal and mutation operations cannot be applied to these named properties.
That dot notation is actually assigning a property to the array, not pushing a new value!
const myArray = ['apples', 'bananas'];
console.log(myArray);
myArray[2] = 'peach';
console.log(myArray);
I assume this is what made you look towards objects, which do this for assignment:
const myObject = {
id: 1,
name: 'Pig',
type: 'animal',
}
// Standard dot-notation assignment
myObject.sound = 'Oink!';
console.log(myObject);
// "Computed" assignment
myObject['emoji'] = '🐷';
console.log(myObject);
Here's a good read on the topic above https://ui.dev/computed-property-names/.
Back to the question at hand, though: why can't I do this:
const myArray = ['choclate', 'strawberry'];
myArray.pinapple = 'Tasty';
Arrays are essentially lists. It doesn't make sense to add an attribute (i.e. "describer") to a list.
Don't get me wrong - it is perfectly alright to set properties of an Array (as it is based off JavaScript objects), but it isn't used in the way that you're thinking of.
Here's an example of when I might use the "dot notation" assignment for an Array:
let zoo = ['Dolphin', 'Lion', 'Monkey'];
console.log(zoo);
// In-built property
console.log('I have', zoo.length, 'animals in my zoo!');
// Now, let's add a property "income"
zoo.income = 500;
console.log('My zoo has £%s', zoo.income);
// We can use this like a normal object property
zoo.income += 50;
console.log('My zoo has £%s', zoo.income);
// Let's create a method for when the zoo goes out of business
zoo.closeDown = () => {
zoo = [];
zoo.income = 0;
return true;
}
zoo.closeDown();
console.log(zoo);
console.log('My zoo has £%s', zoo.income);
Why would I want to do this? In this example, I could've used an object. But it's possible that it makes more sense to keep the animals in my zoo as an array, and then build up the methods and properties from there.
Okay, but how do I get a list of these properties/methods then?
const myArray = ['Foo', 'Bar'];
myArray.isCool = true;
console.log(myArray);
console.log(myArray.length);
let i;
for (i in myArray) {
console.log(i, myArray[i]);
}
The for (i in ...) syntax can be used here, as we are iterating through the properties of the array as an object. As we know from before, Arrays extend the Object class (kind of).
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in
length only counts the numeric properties.
Arrays are objects, so you can add other properties to them.
You don't even have to use dot notation, you can write arr["dot"] = "notation";
I have an array of objects in javascript, something like
var arrayobj = [{a:'a'},{b:'b'},{c:'c'}] (but with more complex values).
Now I check the index of some object manually, like arrayobj[1]
And I got Object {b: "b"}
Now I type arrayobj.indexOf({b:'b'});
and the response is -1 (aka not found).
Anyone could tell me why this happens? I have read the documentation on indexOf method and arrays, but I still have no clue.
Thanks in advance!
indexOf checks for equality (specifically strict equality, ===, but it doesn't matter for this question). Two different objects are not equal to each other even if they have the same properties. The object in your array and the one you pass to indexOf are different objects, and so they don't match.
If you searched for the same object, it would find it:
var b = {b:'b'};
var arrayobj = [{a:'a'},b,{c:'c'}];
console.log(arrayobj.indexOf(b)); // 1
The indexOf method returns the first index at which a given element can be found in the array, or -1 if it is not present.
arrayobj is an array of objects, In first case arrayobj[1] it will return the element at index 1.
In second case arrayobj.indexOf({b:'b'}) is not referring to the object which in the arrayobj but it is a different object.
arrayobj.indexOf({b:'b'})
You are supplying a new object in the indexOf method although contents are same. IndexOf will check for strict equality so references of the objects need to be the same
When you call arrayobj.indexOf({b:'b'}) the Object {b:'b'} is different from the object in your array. These 2 objects have the same "value" but are distinct in the memory.
For exemple if you do this :
var object = {b:'b'}
var arrayobj = [{a:'a'},object,{c:'c'}]
or
var arrayobj = [{a:'a'},{b:'b'},{c:'c'}]
var object = arrayobj[1]
arrayobj.indexOf(object) will return 1 because it's really the same object and not a "copy"
Javascript IndexOf function will work only for value types and not for reference types in your case. Like when you are passing {b:'b'} to indexOf function then it is all in all a complete different object / reference. If you first fetch object from list and then check that reference in list then it will definitely return greater then -1 if object / reference exist
var arrayobj = [{a:'a'},{b:'b'},{c:'c'}];
var obj = arrayobj[1];
console.log(arrayobj.indexOf(obj)); console.log(arrayobj.indexOf({b:'b'}));
you can use for loop instead of built-in function.
This question already has answers here:
Sort Keys in Javascript Object
(2 answers)
Closed 2 years ago.
I have an object
{
"William_Hill":{},
"bet365":{},
"royal panda":{"pay":0},
"karamba":{"roller":0,"braned":0,"pay":0},
"betfred":{"braned":0}
}
and want to sort it by the nuber of properties, so the result shoul look like this and it must be an object
{
"William_Hill":{},
"bet365":{},
"betfred":{"braned":0},
"royal panda":{"pay":0},
"karamba":{"roller":0,"braned":0,"pay":0}
}
I have sorted it like this one but the result is an array
var keysSorted = Object.keys(resultArray).sort(function(a,b){
return Object.keys(resultArray[a]).length-Object.keys(resultArray[b]).length;
});
Until ES2015, JavaScript object properties had no defined order. They do now, but that order isn't respected by for-in or Object.keys, only by new features like Object.getOwnPropertyNames. It's also more complicated than it seems and of very limited use.
There's almost never any good reason to try to put an object in a specific order. If you want order, use an array, or in ES2015 use a Map (see below).
But that said, in ES2015, with property names like yours, it's possible: The only way to set the order of the properties in an object is to determine the order in which they're created, which (for properties that aren't array indexes) is the order they'll be in. So you can do this by getting all the property names, determining the order you want them in, and then creating a new object, adding the properties in that order.
Beware, though, that certain classes of property names (what we call "array indexes") are sorted differently. None of your property names meets the definition, but (say) "123" would.
Here's an example of doing it, if you really want to (I've happily used ES2015's arrow functions, since this only works in ES2015):
// The object
let obj = {
"William_Hill":{},
"bet365":{},
"royal panda":{"pay":0},
"karamba":{"roller":0,"braned":0,"pay":0},
"betfred":{"braned":0}
};
// Get its names, sort them by how may sub-props there are
let names = Object.keys(obj);
names.sort((a, b) => Object.keys(obj[a]).length - Object.keys(obj[b]).length);
// Get the new object
let newObj = names.reduce((acc, name) => {
acc[name] = obj[name];
return acc;
}, {});
// Show its property names
console.log(Object.getOwnPropertyNames(newObj));
Naturally, that can be shorter, I've kept the steps separate for clarity. Similarly, I go back and forth on that use of reduce, you could do the last bit like this instead:
// Get the new object
var newObj = {};
names.forEach(name => {
acc[name] = obj[name];
});
Here's an example using a Map instead, which offers much less complicated ordering of entries (no worries about "array indexes"):
// The object
let obj = {
"William_Hill":{},
"bet365":{},
"royal panda":{"pay":0},
"karamba":{"roller":0,"braned":0,"pay":0},
"betfred":{"braned":0}
};
// Get its names, sort them by how may sub-props there are
let names = Object.keys(obj);
names.sort((a, b) => Object.keys(obj[a]).length - Object.keys(obj[b]).length);
// Get the map
let map = new Map();
names.forEach(name => {
map.set(name, obj[name]);
});
// Show its property names
console.log(Array.from(map.keys()));
Dict object in Javascript cannot guarantee the element order.
So maybe you can only use an array to handler the sorted result.
Reference: Does JavaScript Guarantee Object Property Order?
Sorry for asking a noob question but if I have an array:
MyArray["2cd"]="blah1";
MyArray["3cx"]="blah3";
MyArray["8cz"]="blah2";
And a string myStr="2cd";
And then I use MyArray[myStr] to get the value of blah, how can I get the number I am accessing in the object/array or 0 in this case?
If I may read between the lines, it sounds like you're thinking that the code you posted:
MyArray["2cd"] = "blah1";
MyArray["3cx"] = "blah3";
MyArray["8cz"] = "blah2";
will automatically become the equivalent of:
MyArray[0] = MyArray["2cd"] = "blah1";
MyArray[1] = MyArray["3cx"] = "blah3";
MyArray[2] = MyArray["8cz"] = "blah2";
and therefore you can get the string "blah1" either of these two ways:
var foo = MyArray[0]; // sets foo to "blah1"
var bar = MyArray["2cd"] // also sets bar to "blah1"
But that's not how JavaScript works.
You certainly can set things up so you can use my MyArray[0] and MyArray["2cd"] to fetch the same value, but you have to do it explicitly as in my example.
One thing you didn't mention is how you declared MyArray itself. Is it an Array or an Object? That is, before the code you posted, did you create MyArray with:
var MyArray = {}; // or equivalently, var Array = new Object;
or:
var MyArray = []; // or equivalently, var Array = new Array;
The first example creates an Object, the second an Array.
What is a bit confusing is that JavaScript has both of these two types, which in many cases you can use somewhat interchangeably. But it's customary to use an Object when you are using arbitrary (generally but not necessarily non-numeric) keys into the object, as in your example. Conversely, it's customary to use an Array when you are primarily using strictly numeric indexes.
In fact, JavaScript's Array type inherits from the Object type. An Array is simply an Object with some additional behavior:
An Array has additional methods such as .push() which appends an item to the array.
An Array has a .length property which is automatically updated when you add elements with .push() or a direct array[123] assignment, or when you remove elements with .pop() or other methods.
What JavaScript doesn't have, as Fabrício pointed out, is an "associative array" that behaves like what you might find in some other languages. It has Objects and it has Arrays (which inherit from Objects), and you have to deal with each of those on their own terms.