Shopping Basket indexOf - javascript

I am trying to use indexOf to determine if the obj I am passing it is already within the array. It does not seem to work. Here is the code for adding the item to the array.
Javascript:
add: function(id, item, description, price, itemObj) {
if(items.indexOf(itemObj) == 1) {
console.log(itemObj);
items.forEach(function(item){
if(item.id === id){
item.itemCount += 1;
};
});
} else {
console.log(itemObj);
items.push(new basket.Item(id, item, description, price));
};
basket.print();
}
To be more descriptive, it will never run the if statement and will always resort to passing a new object to the array.

I'm not sure if indexOf looks for matching a value in an array. I think it just matchs a substring inside a string. Plus, you should check if the returned value is greater or equal zero:
if(items.join("").indexOf(itemObj) >= 0) {
Well, the join function concatenates the values to build a string, example:
items = ["first", "second", "third"];
items.join("");
// now items = "firstsecondthird";
var ind = items.join("").indexOf("first"); //ind here is greater or equal than 0
var ind = items.join("").indexOf("firstone"); //ind here is equal -1 (any comparison in the string)
Be aware that indexOf is searching for the first comparison in the string. If you have this string abcdefga and you looking for a with indexOf, the returned value will be 0 because the first comparison is at the beginning. To search for the last comparison would be lastIndexOf.

Related

Get get unique ObjectID's from an array containing duplicates

So I have an array of ObjectID's, for example:
console.log(objectIdArray);
gives [ObjectID, ObjectID, ObjectID].
However there are duplicates here, as seen when mapping to ID strings:
var idArray = objectIdArray.map(objectId => objectId.toString());
console.log(idArray);
gives ["5afa54e5516c5b57c0d43227", "5afa54e5516c5b57c0d43227", "5afa54f0516c5b57c0d43228"] where you can see the ID ending in 27 is duplicated twice.
How can I filter this array of ObjectID's to remove the duplicates (keeping the complete ObjectID objects, not just the ID string values)?
const removeDuplicates = inputArray => {
const ids = [];
return inputArray.reduce((sum, element) => {
if(!ids.includes(element.toString()){
sum.push(element);
ids.push(element.toString());
}
return sum;
}, []);
};
This solution will remove all the objects that aren't the first object with a certain Id.
We fill an Array with the ids, then we check if the ids already are filled in the current list.
The above solution can be potentially slow if there is a lot of elements since you need to check the list of ids which is O(n) for each iteration in the inputArray which would put the algorithm at O(n^2)+O(n)
So instead, we can sort it first based on toString() then we can just verify that the current id didn't match the last id we saw.
const removeDuplicates = inputArray => {
const sortedArray = inputArray.sort((a,b) => (a.toString() > b.toString() ? 1 : (a.toString() < b.toString() ? -1 : 0)));
let lastSeen = undefined;
return sortedArray.reduce((sum, element) => {
if(lastSeen !== element.toString()){
sum.push(element);
}
lastSeen = element.toString();
return sum;
}, []);
};
Now the algorithm is O(n log n) + O(n) assuming sort uses Merge Sort
If you use ES6 you can take Sajeetharan's answer, but create a set of objects, not their ids:
let nodupes = [...new Set(objectIdArray)];
I recommend MongoDB Aggregation Pipeline to prevent the situation of having a final result with duplicate ObjectId values.
However:
// Define callback Function to receive modified Array
var receiveModifiedArray = function(objectIdArray) {
// log modified Array to console
console.log(objectIdArray);
}
// Remove duplicate ObjectId values
function removeDuplicateObjectIdValues(objectIdArray, callback) {
// Iterate through each ObjectId
objectIdArray.forEach((currentValue, index, array) => {
// evaluate Array items with index greater than 0
if(index > 0) {
// check ObjectId string values for type and value equality
if(currentValue.toString() == array[index -1].toString()) {
/**
* The splice() method changes the contents of an array
* by removing existing elements and/or adding new elements.
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
*/
objectIdArray.splice(index,1);
}
// If processing last item in Array, callback
if(index == array.length +1) {
callback(objectIdArray);
}
}
});
// Return to move on to next message in call stack
return;
}
// Remove duplicate ObjectId values
removeDuplicateObjectIdValues(objectIdArray,receiveModifiedArray);

Can anybody explain how exactly Filter and IndexOf methods work together in a function?

I have the following function, which pretty much does what it supposed to, but I would like to understand exactly what it does on each steps of its loop.
Could you please take a look to the function below and give me a clear explanation commenting each step or the Filter and IndexOf methods?
Thank you very much in advance.
var arr = [6,2,6,8,9,9,9,4,5];
var unique = function(){
return arr.filter(function(e, i, a) {
return i === a.indexOf(e);
})
}
unique();
indexOf returns the first index of an element in an array:
[1,2,2,3].indexOf(2); // 1
So if you use filter as in your example when it gets to the second occurance of an element the index (i in your example) will not be equal to the value returned by indexOf and be dropped. In my array above the second 2 is at position 2 which obviously doesn't strictly equal the one returned by indexOf.
[1,2,2,3].filter((value, index, array) => array.indexOf(value) === index);
// first iteration: value is 1, index is 0, indexOf is 0 0===0 keep 1
// second: value is 2, index is 1, indexOf is 1, 1===1 keep 2
// third: value is 2, index is 2, indexOf is 1, 1===2 false! toss 2
// etc.
The end effect is that any duplicate elements get dropped from the copy returned by filter. And it is a copy, the original array is not mutated.
EDIT
I should probably mention that recent versions of JavaScript give us a better way:
let arrayWithDupes = [1,2,2,3];
let uniq = Array.from(new Set(arrayWithDupes)); // [1,2,3]
If log the values like:
var arr = [6,2,6,8,9,9,9,4,5];
var unique = function(){
return arr.filter(function(e, i, a) {
console.log('e: ' + e);
console.log('i: ' + i);
console.log('a: ' + a);
return i === a.indexOf(e);
})
}
var unq = unique();
console.log(unq);
you will get:
"e: 6"
"i: 0"
"a: 6,2,6,8,9,9,9,4,5"
and so on...
e = current element from array, i = index of the array, a = array source;
Filer function: "The filter() method creates an array filled with all array elements that pass a test (provided as a function)."
indexOf: "The indexOf() method searches the array for the specified item, and returns its position."

Get first element in array with index not starting from 0

I'm using a javascript library which returns arrays not starting from zero like starting from 26 or 1500, what i want to do is a method to get the first element in that array regardless of the index number starting with 0 or any other number.
Are they any method to do this in javascript ?
I suggest to use Array#some. You get the first nonsparse element and the index. The iteration stops immediately if you return true in the callback:
var a = [, , 22, 33],
value,
index;
a.some(function (v, i) {
value = v;
index = i;
return true;
});
console.log(index, value);
The information below is generally useful, but for the problem the OP listed, Nina's answer is by far a better solution.
Those are called sparse arrays and they're one of the few situations where you may want to use for-in on an array.
Remember that arrays are objects in JavaScript, and array entries are properties keyed by names (array indexes) that meet certain criteria. So we can use the features that let us discover the properties on an object to find the indexes on your sparse array.
for-in example:
for (var n in theArray) {
if (theArray.hasOwnProperty(n) && isArrayIndex(n)) {
// Use theArray[n]
}
}
This answer shows how you can determine that n is an array index as opposed to being some other property. A very technical definition would be
function isArrayIndex(n) {
return /^0$|^[1-9]\d*$/.test(n) &&
n <= 4294967294;
}
...but a definition that's good enough for most of us would be
function isArrayIndex(n) {
return !isNaN(parseInt(n, 10));
}
Similarly, you can use Object.keys; since it only looks at own enumerable properties, you don't need the hasOwnProperty check:
Object.keys(theArray).forEach(function(n) {
if (isArrayIndex(n)) {
// ...
}
});
Note that officially, neither of those is in any particular order, not even in ES2015 ("ES6"). So in theory, you could see the indexes out of numeric order. In the real world, I've never seen an even vaguely-modern JavaScript engine that returned array indexes out of order. They're not required to, but every one I've tried does.
So officially, you would need to get a full list and then find the minimum value in it:
var min = Object.keys(theArray).reduce(function(min, n) {
var i = parseInt(n, 10);
return isNaN(i) || (min !== undefined && min > i) ? min : i;
}, undefined);
That'll given you undefined if the array is empty, or the min index if it isn't. But if you want to make the assumption you'll get the keys in numeric order:
// Makes an assumption that may not be true
var min = +Object.keys(theArray).filter(isArrayIndex)[0];
If you're using a JavaScript engine that's entirely up-to-date, you can rely on the order returned by Object.getOwnPropertyNames, which is required to list the array indexes in order.
var min = +Object.getOwnPropertyNames(theArray).filter(isArrayIndex)[0];
It may be useful to use a filter function on the array to get back a normalised array.
var fullArray = array.filter(function(n){
return n != undefined;
});
fullArray[0]
The answers here may help you decide Remove empty elements from an array in Javascript
I guess one alternative to Array.prototype.some() is the Array.prototype.findIndex() method. These are much faster than filter alone and will keep your array and indices untouched.
var arr = new Array(1000),
fi = -1;
arr[777] = 1453; // now we have a nice sparse array
fi = arr.findIndex(f => f !== void 0); // void 0 is the perfect undefined
console.log(fi);
console.log(arr[fi]);
With this piece of code you can find first assigned value index and then get the value from your array:
var a = [, , 22, 33];
var value = a.find((v, i) => i in a);
console.log(value);
/* ---------------------------------------------- */
var i = 0
while (!(i in a) && i < a.length) i++; // If i === a.length then the array is emtpy
console.info(i, a[i]);
First implementation uses Array.prototype.find which makes less variable usage so this is cleaner but to find the index you should call indexOf over the array.
But the second one is a little bit old fashioned but give the chance of having index without extra efforts.
BTW Nina's seems better. (can make it shorter?)
const arr = [0,1,2]
// using destructuring to get the first element
let [first] = arr
// plus: using destructuring to get the last element
let [first] = [...arr].reverse()

Javascript: Find douplicated values from array with keys

Title is pretty much self explanatory...
I want to be able to find duplicated values from JavaScript array.
The array keys can be duplicated so I need to validate only the array values.
Here is an example :
var arr=[
Ibanez: 'JoeSatriani',
Ibanez: 'SteveVai',
Fender: 'YngwieMalmsteen',
Fender: 'EricJohnson',
Gibson: 'EricJohnson',
Takamine: 'SteveVai'
];
In that example:
the key is the guitar brand
the value is the guitar player name.
So:
If there is duplicated keys (like: Ibanez or Fender) as on that current example that is OK :-)
But
If there is duplicated values (like: EricJohnson or SteveVai) I'm expecting to get (return) that error:
EricJohnson,SteveVai
You can't have associative arrays in Javascript. You can create an array of objects, like:
var arr=[
{Ibanez: 'JoeSatriani'},
{Ibanez: 'SteveVai'},
{Fender: 'YngwieMalmsteen'},
{Fender: 'EricJohnson'},
{Gibson: 'EricJohnson'},
{Takamine: 'SteveVai'}
];
Then you'll need a for...in loop to go over every object in the array, create a new array of values and check that for duplicates, which is also not very straightforward - basically you'll want to sort the array and make sure no value is the same as the one after it.
var arrayOfValues = [];
arr.forEach(function(obj){
for(var prop in obj)
arrayOfValues.push(obj[prop]);
});
arrayOfValues.sort(); // by default it will sort them alphabetically
arrayOfValues.forEach(function(element,index,array){
if(array[index+1] && element==array[index+1])
alert("Duplicate value found!");
});
First of all, object keys can not be repeated.
This means that:
({
"Fender": "Jim",
"Fender": "Bob"
})["Fender"]
Would simply return: "Bob".
However, I did make a code that could allow you to find duplicates in values, but as I said, the key will have to be unique:
var arr = {
Ibanez: 'EricJohnson',
Fender: 'YngwieMalmsteen',
Gibson: 'EricJohnson',
Takamine: 'SteveVai',
"Takamine2": 'SteveVai'
};
function contains(a, obj) {
for (var i = 0; i < a.length; i++) {
if (a[i] === obj) {
return true;
}
}
return false;
}
var track = [];
var exists = [];
for (var val in arr) {
if (contains(track, arr[val])) {
exists.push(arr[val]);
} else {
track.push(arr[val])
}
}
alert(exists)
You can see it working here: http://jsfiddle.net/dr09sga6/2/
As others have commented, the example array you provided isn't a valid JavaScript array. You could, however, keep a list for each guitar type:
var mapping = {
Ibanez: ['JoeSatriani','SteveVai'],
Fender: ['YngwieMalmsteen','EricJohnson']
Gibson: ['EricJohnson'],
Takamine: ['SteveVai']
];
Or a list of each guitar/musician pair:
var pairs = [
['Ibanez','JoeSatriani'],
['Ibanez','SteveVai'],
['Fender','YngwieMalmsteen'],
['Fender','EricJohnson'],
['Gibson','EricJohnson'],
['Takamine','SteveVai']
];
Your solution is going to depend on which pattern you go with. However, in the second case it can be done in one chained functional call:
pairs.map(function(e) {return e[1]}) // Discard the brand names
.sort() // Sort by artist
.reduce(function(p,c,i,a){
if (i>0 && a[i]==a[i-1] && !p.some(function(v) {return v == c;})) p.push(c);
return p;
},[]); //Return the artist names that are duplicated
http://jsfiddle.net/mkurqmqd/1/
To break that reduce call down a bit, here's the callback again:
function(p,c,i,a){
if (i>0
&& a[i]==a[i-1]
&& !p.some(function(v) {
return v == c;
}))
p.push(c);
return p;
}
reduce is going to call our callback for each element in the array, and it's going to pass the returned value for each call into the next call as the first parameter (p). It's useful for accumulating a list as you move across an array.
Because we're looking back at the previous item, we need to make sure we don't go out of bounds on item 0.
Then we're checking to see if this item matches the previous one in the (sorted) list.
Then we're checking (with Array.prototype.some()) whether the value we've found is ALREADY in our list of duplicates...to avoid having duplicate duplicates!
If all of those checks pass, we add the name to our list of duplicate values.

Query if string is in any one of a set of arrays

I have a function written in JQuery. I have some arrays, like so:
var array1 = ["pink","yellow","green","red"],
array2 = ["peony","violet","rose","daisy"],
array3 = ["dalmation","kelpie","shepard","dachshund"],
I have a value from an input, called
$query
I would like to find out if the value of $query is equal to the value of any of the elements of any of the 3 arrays. If yes, I would like to know which array it is. An index number (ie 0 for array1, 1 for array2) would work fine.
This JSFiddle here http://jsfiddle.net/hJrAA/ from this StackOverflow question Search multiple arrays for a string and then set a var - jQuery is tantalising, but no matter what I change the value of the input query to,
console.log(inArrays('red', first, second, third));
(where 'red' is the query) it always returns the same value (2).
Can anyone suggest a way to modify that code so that it returns a reference to the specific array that the query was found in?
If you just wanted to return the index of the first array that was found I would do this:
function inArrays(val) {
var arrays = Array.prototype.slice.call(arguments, 1);
var idx = -1;
var i = 0;
while (i < arrays.length) {
if ($.inArray(val, arrays[i]) > -1) {
return i; // Breaks out of the loop as soon as a match is found
}
i++;
}
return idx;
}
Two things are different here from your fiddle
$.inArray is returning an index, not True or False, so if it's found the index will be >= 0. If an index is not found it will be -1.
I do a return i instead of setting the index and continuing. This not only breaks out of the loop earlier, it will return the index of the first match. Your original code always will return the last match.
http://api.jquery.com/jQuery.inArray/
The $.inArray() method is similar to JavaScript's native .indexOf()
method in that it returns -1 when it doesn't find a match. If the
first element within the array matches value, $.inArray() returns 0.
Because JavaScript treats 0 as loosely equal to false (i.e. 0 ==
false, but 0 !== false), if we're checking for the presence of value
within array, we need to check if it's not equal to (or greater than)
-1.
if ($.inArray(val, arrays[i]) > -1)

Categories

Resources