Detecting if a property value exists in an array of objects - javascript

I am trying to check if a 'member' object with a specific 'ID' already exists in the 'members' array of a container 'EntityGroup' object. why doesn't the following EntityGroup.idExists(id) work:
EntityGroup = function() {
this.members = []; // intention is for this to hold 'Entity' objects
this.classType = null; // what class of entities does it hold
};
EntityGroup.prototype = {
addEntity: function(entityType, EntityID) {
// TODO implement .idExists() check here
// dont add new member if the id does exist
this.members.push(new Entity(entityType, EntityID))
},
idExists: function(EntityID) {
var idExists = false,
member,
members = this.members;
for (member in members) {
if (EntityID == member.EntityID) {
idExists = true;
break;
} else {
continue;
}
}
return idExists;
}
};
Entity = function(entityType, EntityID) {
this.EntityID = EntityID;
this.entityType = entityType;
};
g = new EntityGroup();
g.addEntity("Person", 1);
g.addEntity("Person", 2);
console.log(g.idExists(1)); // returns false which is not expected
console.log(g.members);

for (x in y) is not the right construct to iterate through objects in an array. It is meant to be used for iterating through keys of an object only.
So what is happening is that instead of getting the two Entity objects, the member variable is referring to the index of those objects which is 1 and 2 respectively. The correct way to iterate through those objects is:
for(var i = 0; i < members.length; i++) {
EntityID == members[i].EntityID;
}

The problem is your for...in loop. You should only use for...in when iterating over properties in an object, not through items of an array.
If you replace this loop with the following, you should be fine:
for(var i=0,len=members.length; i<len; ++i){
var member = members[i];
//the rest

Related

Browse associative array as circular list

I would like to browse an associative array like a circular list.
First the associative array is defined like this :
array = {item1:array(...), item2:array(...), ...}
When at the first element I browse the array of this element, once arrive at the last element of this array it should passe to the second element and brows it's array, and the same for the last one who must return to the first element.
so I initialize my array as follows:
// Build the associative array
Prot.prototype.additem = function(itemName, itemArray)
{
this.array[itemName] = itemArray; // itemArray is an array
}
// Init the currentItem of the associative array to browse (We don't necessarily start at the first)
Prot.prototype.init = function(itemName)
{
this.currentItem = this.array[itemName];
this.currentItemArray = 0;
}
Prot.prototype.next = function()
{
// here I browse the first array of the first key of my associative array
var index = this.currentItem.indexOf(this.currentItemArray);
index = index +1;
this.currentItemArray = this.currentItem[index];
if (index == (this.currentItemArray.length - 1))
{
// when arrives at the last element of the array of the first key I should pass to the second
return false;
}
else {
return true;
}
}
// I add a set interval at the end so no need for a loop
You'll need an array to know what is the "next" item array. So I would suggest storing the desired order in another array, having just those names.
Here is a possible implementation:
class Prot {
constructor() {
this.itemNames = [];
this.arrays = {};
this.hasData = false;
this.currentIndex = 0;
}
additem(itemName, itemArray) {
if (itemName in this.arrays) throw "duplicate entry";
this.arrays[itemName] = { data: itemArray, index: this.itemNames.length };
this.itemNames.push(itemName); // keep the order
if (itemArray.length) this.hasData = true;
}
init(itemName) {
this.currentItem = this.arrays[itemName];
this.currentIndex = 0;
}
next() {
if (!this.hasData) return;
if (!this.currentItem) this.currentItem = this.arrays[this.itemNames[0]];
var data = this.currentItem.data[this.currentIndex++];
while (this.currentIndex >= this.currentItem.data.length) {
this.currentItem = this.arrays[this.itemNames[(this.currentItem.index+1) % this.itemNames.length]];
this.currentIndex = 0;
}
return data;
}
}
// demo
let obj = new Prot;
// add the arrays:
obj.additem("a", [1, 2, 3]);
obj.additem("b", [4, 5]);
obj.additem("c", [6, 7, 8, 9]);
obj.additem("d", [0]);
// Start at "b":
obj.init("b");
// iterate from there...
for (let i = 0; i < 12; i++) {
console.log(obj.next());
}
There is no such thing as an associative array in JavaScript, but you can use an object instead. A very simple implementation of defining an object and referencing its properties in a circular way would be the following:
// define the object with 6 properties and assiociated values:
var obj={a:123, b:456, c:789, d:666, e:777, f:888};
function getcirc(obj){
// use a "static variable" inside the function:
if(typeof getcirc.i=="undefined") getcirc.i=0;
var keys=Object.keys(obj), k=keys[getcirc.i++%keys.length];
console.log(k,obj[k]);
}
// call the function repeatedly ...
for (var n=0;n<20;n++) getcirc(obj);

What is the most efficient way to search through a list of JavaScript objects and determine whether they contain the same values?

I have the following function which takes some values that the user has entered and creates JavaScript objects from those values, then puts those objects in an array and returns the array:
function createObjects() {
var objectArray = [];
for (var i = 0; i < someCount; i++) {
var object = {
property1: someProperty1,
property2: someProperty2,
property3: someProperty3,
property4: someProperty4
};
objectArray.push(object);
}
return objectArray;
}
Now, I want to compare these objects' properties and determine whether any two contain all of the same values for property1, property2, property3, and property4. If any two of these objects have all four of the same values for these properties, I want a validation check to return false. Here is what I have so far:
function objectsAreUnique() {
var objects = createObjects();
for(var i = 0; i < objects.length; i++) {
//need to determine whether all four of the properties are the same for any two objects
//if(objectsAreSame) { return false; }
}
return true;
}
I have a few ideas, but I'm interested to see what is the most efficient way to achieve this. Thanks!
If you can guarantee that the properties will always be inserted in the same order (which will be the case if using an object literal as in your example), you can do this in ~O(n) using JSON.stringify and a Set:
function objectsAreUnique() {
const objects = createObjects();
return (new Set(objects.map(o => JSON.stringify(o)))).size == objects.length;
}
First, create a function to test whether two objects are the same. You can enter each property individually, or get creative with the JSON.stringify function
properties individually:
function objectsIdentical(obj1, obj2) {
return obj1.property1 == obj2.property1 && obj1.property2 == obj2.property2 && obj1.property3 == obj2.property3 && obj1.property4 == obj2.property4;
}
JSON.stringify (recommended for objects with many properties)
function objectsIdentical(obj1, obj2) {
return JSON.stringify(obj1).replace(/^.|.$/g, "").split(",").sort().join(",") == JSON.stringify(obj2).replace(/^.|.$/g, "").split(",").sort().join(",");
}
Then, you can use a for loop to check if any of them are identical.
for (let i=0; i<objects.length-1; i++) {
for (let j=i+1; j<objects.length; j++) {
if (objectsIdentical(objects[i], objects[j])) {
return false;
}
}
}
return true;
If you're familiar with the "some" function, you can use that.
return !objects.some((v, i) => objects.slice(i+1).some(w => objectsIdentical(v, w)))
function objectsAreUnique() {
var objects = createObjects();
var stringifiedAndSorted = objects.map(obj => JSON.stringify(obj)).sort()
for(var i = 0; i < stringifiedAndSorted.length-1; i++) {
if(i === i+1)
return false;
}
return true;
}

push only unique elements in an array

I have array object(x) that stores json (key,value) objects. I need to make sure that x only takes json object with unique key. Below, example 'id' is the key, so i don't want to store other json objects with 'item1' key.
x = [{"id":"item1","val":"Items"},{"id":"item1","val":"Items"},{"id":"item1","val":"Items"}]
var clickId = // could be "item1", "item2"....
var found = $.inArray(clickId, x); //
if(found >=0)
{
x.splice(found,1);
}
else{
x.push(new Item(clickId, obj)); //push json object
}
would this accomplish what you're looking for? https://jsfiddle.net/gukv9arj/3/
x = [
{"id":"item1","val":"Items"},
{"id":"item1","val":"Items"},
{"id":"item2","val":"Items"}
];
var clickId = [];
var list = JSON.parse(x);
$.each(list, function(index, value){
if(clickId.indexOf(value.id) === -1){
clickId.push(value.id);
}
});
You can't use inArray() because you are searching for an object.
I'd recommend rewriting a custom find using Array.some() as follows.
var x = [{"id":"item1","val":"Items"},{"id":"item1","val":"Items"},{"id":"item1","val":"Items"}]
var clickId = "item1";
var found = x.some(function(value) {
return value.id === clickId;
});
alert(found);
Almost 6 years later i ended up in this question, but i needed to fill a bit more complex array, with objects. So i needed to add something like this.
var values = [
{value: "value1", selected: false},
{value: "value2", selected: false}
//there cannot be another object with value = "value1" within the collection.
]
So I was looking for the value data not to be repeated (in an object's array), rather than just the value in a string's array, as required in this question. This is not the first time i think in doing something like this in some JS code.
So i did the following:
let valueIndex = {};
let values = []
//I had the source data in some other and more complex array.
for (const index in assetsArray)
{
const element = assetsArray[index];
if (!valueIndex[element.value])
{
valueIndex[element.value] = true;
values.push({
value: element.value,
selected: false
});
}
}
I just use another object as an index, so the properties in an object will never be repated. This code is quite easy to read and surely is compatible with any browser. Maybe someone comes with something better. You are welcome to share!
Hopes this helps someone else.
JS objects are great tools to use for tracking unique items. If you start with an empty object, you can incrementally add keys/values. If the object already has a key for a given item, you can set it to some known value that is use used to indicate a non-unique item.
You could then loop over the object and push the unique items to an array.
var itemsObj = {};
var itemsList = [];
x = [{"id":"item1","val":"foo"},
{"id":"item2","val":"bar"},
{"id":"item1","val":"baz"},
{"id":"item1","val":"bez"}];
for (var i = 0; i < x.length; i++) {
var item = x[i];
if (itemsObj[item.id]) {
itemsObj[item.id] = "dupe";
}
else {
itemsObj[item.id] = item;
}
}
for (var myKey in itemsObj) {
if (itemsObj[myKey] !== "dupe") {
itemsList.push(itemsObj[myKey]);
}
}
console.log(itemsList);
See a working example here: https://jsbin.com/qucuso
If you want a list of items that contain only the first instance of an id, you can do this:
var itemsObj = {};
var itemsList = [];
x = [{"id":"item1","val":"foo"},
{"id":"item2","val":"bar"},
{"id":"item1","val":"baz"},
{"id":"item1","val":"bez"}];
for (var i = 0; i < x.length; i++) {
var item = x[i];
if (!itemsObj[item.id]) {
itemsObj[item.id] = item;
itemsList.push(item);
}
}
console.log(itemsList);
This is late but I did something like the following:
let MyArray = [];
MyArray._PushAndRejectDuplicate = function(el) {
if (this.indexOf(el) == -1) this.push(el)
else return;
}
MyArray._PushAndRejectDuplicate(1); // [1]
MyArray._PushAndRejectDuplicate(2); // [1,2]
MyArray._PushAndRejectDuplicate(1); // [1,2]
This is how I would do it in pure javascript.
var x = [{"id":"item1","val":"Items"},{"id":"item1","val":"Items"},{"id":"item1","val":"Items"}];
function unique(arr, comparator) {
var uniqueArr = [];
for (var i in arr) {
var found = false;
for (var j in uniqueArr) {
if (comparator instanceof Function) {
if (comparator.call(null, arr[i], uniqueArr[j])) {
found = true;
break;
}
} else {
if (arr[i] == uniqueArr[j]) {
found = true;
break;
}
}
}
if (!found) {
uniqueArr.push(arr[i]);
}
}
return uniqueArr;
};
u = unique(x, function(a,b){ return a.id == b.id; });
console.log(u);
y = [ 1,1,2,3,4,5,5,6,1];
console.log(unique(y));
Create a very readable solution with lodash.
x = _.unionBy(x, [new Item(clickId, obj)], 'id');
let x = [{id:item1,data:value},{id:item2,data:value},{id:item3,data:value}]
let newEle = {id:newItem,data:value}
let prev = x.filter(ele=>{if(ele.id!=new.id)return ele);
newArr = [...prev,newEle]

Finding MODE Iterating through FOR Loop for multiple arrays

Desired:
I have multiple arrays of players, roundWinners, being iterated through in a FOR loop. For each array, I need the MODE of the array (the most frequently occurring value) assigned to a new variable winner.
I am lost as to where to start, so I ask what is the most efficient way to go about accomplishing this?
Example:
for (i = 0; i < numberOf; i++) {
var roundWinners[0] = ["Joe", "Joe", "Bob", "Sue"]
var winner[0] ="Joe";
var roundWinners[1] = ["Joe", "Sue", "Bob", "Sue"]
var winner[1] ="Sue";
var roundWinners[2] = ["Bob", "Bob", "Bob", "Sue"]
var winner[2] ="Bob";
}
Prior to the loop, create an object.
var ArrMode = {};
Then, on each iteration, take the name, and, using it as a property name, see if the object has a property of that name. If so, increment the value of that property.
If not, add that property, setting its value to 1.
After processing the array, then process the object, using a simple replace-if-greater test. Each time you make a replacement, hold the name of that property as the winner.
Once you have processed the whole object, the property name you are holding is the name of the winner.
I don't actually have an answer
One person posted this as a way to iterate through the properties of a JS object, when you do not know the property names at design time:
for (var property in object) {
if (object.hasOwnProperty(property)) {
// do stuff
}
}
You can add a property to a JS object at any time, using:
obj[theName] = theVal; // the name’d be a string, theVal’d be anything
// Like:
var theName = "John";
obj[theName] = 1;
So, you would create an empty object before the loop with a name (like, say "modes"), but no properties
var modes = {};
Then, in each iteration over the array, iterate through the object's properties, checking the name of the properties against the current name in the array.
If found, then use:
modes[arr[i]]++;
If not found, then use
modes[arr[i]] = 1;
After the iterating over the array, iterate over the object's properties, checking their values, and 'remembering' the name and value of the highest one.
Working:
//Mode Calc
function mode( arr ) {
if ( !Array.isArray( arr ) ) {
throw new TypeError( 'mode()::invalid input argument. Must provide an array.' );
}
var len = arr.length,
count = {},
max = 0,
vals = [],
val;
for ( var i = 0; i < len; i++ ) {
val = arr[ i ];
if ( !count[ val ] ) {
count[ val ] = 0;
}
count[ val ] += 1;
if ( count[ val ] === max ) {
vals.push( val );
} else {
max = count[ val ];
vals = [ val ];
}
}
return vals.sort( function sort( a, b ) {
return a - b;
});
} // end FUNCTION mode()
for (i = 0; i < numberOf; i++) {
winner = ( mode( roundWinners[i] ) );
}

Check if object excists in javascript array

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/

Categories