Getting javascript to search an array within an array - javascript

I have the following javascript to loop through an array of records, and alert the number of matches found within the array, for each field:
mymusic=[{title:"a",artist:"b",artwork:"c",tracks:[{tracktitle:"d",trackmp3:"e"}]}];
tracksArray=[];
trackTitles=[];
var albumScore=0;
var artistScore=0;
var tracksScore=0;
stringToSearchFor="d";
for(i=0;i<mymusic.length;i++){
if((mymusic[i].title).match(stringToSearchFor))
albumScore+=1;
}
if(albumScore!=0)
alert(albumScore+" match(es) found in Albums");
else
alert("No matches found in Albums");
for(d=0;d<mymusic.length;d++){
if((mymusic[d].artist).match(stringToSearchFor))
artistScore+=1;
}
if(artistScore!=0)
alert(artistScore+" match(es) found in Artists");
else
alert("No matches found in Artists");
for(f=0;f<mymusic.length;f++){
tracksArray[f]=mymusic[f].tracks;
for(g=0;g<tracksArray;g++){
trackTitles[g]=tracksArray[g].tracktitle;
}
for(h=0;h<trackTitles.length;h++){
if(trackTitles(h).match(stringToSearchFor))
{
tracksScore+=1;
}
}
}
if(tracksScore!=0)
alert(tracksScore+" match(es) found in Tracks");
else
alert("No matches found in Tracks");
which works fine for the "title" and "artist" records, but always alerts "No matches found" for the "tracks" record, even when there are matches. I guess the problem is with the nested for-loop through the trackTitles array, but I can't see what I can change to make it work. Any ideas?
Thanks

if(trackTitles(h)
You're calling an Array. Should be square brackets.
You could do with breaking out the array handling stuff into reusable functions to improve readability and reduce the number of these stray variables.
Since there are answers with procedural approaches already, here's one based on functional-like array handling for extra fun(*):
function countItemsContaining(seq, prop, str) {
return seq.map(itemGetter(prop)).filter(function(s) {
return s.indexOf(str)!==-1;
}).length;
}
function itemGetter(prop) {
return function(o) {
return o[prop];
};
}
mymusic= [{title:"a",artist:"b",artwork:"c",tracks:[{tracktitle:"d",trackmp3:"e"}]}];
needle= 'd';
var titleScore= countItemsContaining(mymusic, 'title', needle);
var artistScore= countItemsContaining(mymusic, 'artist', needle);
// Calling concat is a JavaScript idiom to combine a load of lists into one
//
var mytracks= [].concat.apply([], mymusic.map(itemGetter('tracks')));
var tracksScore= countItemsContaining(mytracks, 'tracktitle', needle);
array.map and array.filter are standardised in ECMAScript Fifth Edition, but aren't available in IE yet, so for compatibility you can define them like this:
if (!('map' in Array.prototype)) {
Array.prototype.map= function(f, that) {
var a= new Array(this.length);
for (var i= 0; i<this.length; i++) if (i in this)
a[i]= f.call(that, this[i], i, this);
return a;
};
}
if (!('filter' in Array.prototype)) {
Array.prototype.filter= function(f, that) {
var a= [];
for (var i= 0; i<this.length; i++) if (i in this)
if (f.call(that, this[i], i, this))
a.push(this[i]);
return a;
};
}
(*: amount of actual fun contained in answer may be limited)

Take a look at the library called underscore.js. It's made for this kind of stuff. These tasks often come down to a line or two of easy-to-read code.
It uses native methods when available, fills in the missing bits (depending on the browser) and is chainable. It even makes the built-in array methods chainable.

Try this instead:
var tracksScore=0;
stringToSearchFor="d";
for(var f=0;f<mymusic.length;f++){
var tracksArray=mymusic[f].tracks;
for(var g=0;g<tracksArray.length;g++) {
var tracktitle=tracksArray[g].tracktitle;
if(tracktitle.match(stringToSearchFor))
{
tracksScore+=1;
}
}
}
if(tracksScore!=0)
alert(tracksScore+" match(es) found in Tracks");
else
alert("No matches found in Tracks");

You have a number of basic errors which ultimately stem from having too many variables. Here is your code refactored:-
mymusic=[{title:"a",artist:"b",artwork:"c",tracks:[{tracktitle:"d",trackmp3:"e"}]}];
var albumScore=0;
var artistScore=0;
var tracksScore=0;
stringToSearchFor="d";
for (var i=0; i < mymusic.length; i++)
{
if( mymusic[i].title.match(stringToSearchFor))
albumScore += 1;
if( mymusic[i].artist.match(stringToSearchFor))
artistScore += 1;
for (var j = 0; j < mymusic[i].tracks.length; j++)
{
if (mymusic[i].tracks[j].tracktitle.match(stringToSearchFor))
tracksScore += 1
}
}
if (albumScore != 0)
alert(albumScore + " match(es) found in Albums");
else
alert("No matches found in Albums");
if (artistScore != 0)
alert(artistScore + " match(es) found in Artists");
else
alert("No matches found in Artists");
if (tracksScore != 0)
alert(tracksScore+" match(es) found in Tracks");
else
alert("No matches found in Tracks");

What AnthonyWJones and bobince said (although I'll need to spend some time reading through bobince's answer).
An alternative solution: The moment I saw the data structure I thought "recursion!" and thought it'd be fun to see if I could come up with a solution that would work with any size data structure of any (unknown) depth level.
I do not code frequently, so the below may be riddled with bad practices, but it works :). Let me know your thoughts.
myMusic=[{title:"a",artist:"b",artwork:"c",year:"d",tracks:[{tracktitle:"d",trackmp3:"e"}]}];
function find_match(dataObj,stringToSearchFor,resultObj){
resultObj = (resultObj)?resultObj:{}; //init resultObj
for (dataKey in dataObj){ //loop through dataObj
if (typeof(dataObj[dataKey]) == "object"){ //if property is array/object, call find_match on property
resultObj = find_match(dataObj[dataKey],stringToSearchFor,resultObj);
}else if (dataObj[dataKey].match(stringToSearchFor)){ //else see if search term matches
resultObj[dataKey] = (resultObj[dataKey] )?resultObj[dataKey] +=1:1; //add result to resultObj, init key if not yet found, use dataObj key as resultObj key
}
}
return resultObj; //return resultObj up the chain
}
results = find_match(myMusic,"d");
alertString = "";
for (resultKey in results){ //loop through results and construct alert msg.
alertString += results[resultKey] + " match(es) found in " + resultKey + "\n";
}
alert(alertString );

Related

searching for value in an array with a for loop in javascript

I am searching for an value from an array in javascript and this is my code.
HTML
<input type="text" placeholder="enter here" id="textvalue">
<button id="checkbutton">check</button>
Javascript
let arr = ["boy", "cat", "dog"];
let txtval = document.querySelector("#textvalue");
let chkbutt = document.querySelector("#checkbutton");
chkbutt.addEventListener("click", arraycheck);
function arraycheck () {
for(i=0; i<arr.length; i++) {
if(txtval.value==arr[i]) {
alert("match found for " + txtval.value)
}
else {
alert("its not there")
}
}
}
At the moment, I keep getting alert saying "its not there" when I enter "dog". It prints "its not there" twice and then prints "match found for dog".
I would like the alert to show only if the word does not exist in the array. I know it is doing this because I have the if statement in the for loop and each time if the index of the array does not match, it shows the alert. But how do I do it?
You're alerting on every pass of the loop. Instead, use a flag and alert at the end:
function arraycheck () {
var found = false; // *** The flag
for (var i=0; !found && i<arr.length; i++) {
// ^^^^^^^^^---- *** Stop looping if we find it
if (txtval.value==arr[i]) {
found = true; // *** Set the flag, it was found
}
}
if (found) {
alert("match found for " + txtval.value);
} else {
alert("its not there");
}
}
or we could just use the result of the == directly:
function arraycheck () {
var found = false; // *** The flag
for (var i=0; !found && i<arr.length; i++) {
// ^^^^^^^^^---- *** Stop looping if we find it
found = txtval.value==arr[i]; // *** Set the flag if it was found
}
if (found) {
alert("match found for " + txtval.value);
} else {
alert("its not there");
}
}
(Side note: Unless you're declaring i somewhere you haven't shown, your code is falling prey to The Horror of Implicit Globals [that's a post on my anemic little blog]. Declare your variables. I've added declarations for i in the examples above.)
However, there's already a function for that (two, in fact): includes (on newer JavaScript engines) and indexOf (on older ones):
function arrayCheck() {
var found = arr.includes(txtval.value); // <===
if (found) {
alert("match found for " + txtval.value)
} else {
alert("its not there")
}
}
or the found part of that on older browsers using indexOf:
var found = arr.indexOf(txtval.value) != -1;
In a comment you asked:
I was wondering if you could also stop the loop in my code using "return"?
Yes, that's yet another way to do it in this specific case, since exiting the function (with return) exits the loop:
function arraycheck () {
for (var i=0; i<arr.length; i++) {
if (txtval.value==arr[i]) {
alert("match found for " + txtval.value);
return;
}
}
alert("its not there");
}
You can simplify using indexOf method,
if(arr.indexOf(txtval.value) > -1)
DEMO
let arr = ["boy", "cat", "dog"];
let txtval = document.querySelector("#textvalue");
let chkbutt = document.querySelector("#checkbutton");
chkbutt.addEventListener("click", arraycheck);
function arraycheck () {
if(arr.indexOf(txtval.value) > -1)
{
alert("match found");
}
else {
alert("its not there");
}
}
<input type="text" placeholder="enter here" id="textvalue">
<button id="checkbutton">check</button>
Try the following with Array's includes() in a more simpler and cleaner way:
let arr = ["boy", "cat", "dog"];
let txtval = document.querySelector("#textvalue");
let chkbutt = document.querySelector("#checkbutton");
chkbutt.addEventListener("click", arraycheck);
function arraycheck () {
if(arr.includes(txtval.value)){
alert("match found for " + txtval.value)
}
else{
alert("its not there")
}
}
<input type="text" placeholder="enter here" id="textvalue">
<button id="checkbutton">check</button>
Create a variable that will turn to true when a match is found. Then if after the loop the variable is still false, then no match has been found.
Or better yet, use lodash filter. It simplifies the looping process.
You can try to find the index of your txtval.value in the array by using JavaScript Array indexOf() method.
It takes a input value and output the index number of that value in the Array if the value exists.
As javascript array index starts from 0, so if the value exists, the method will return integer(index in the array) value that is greater than -1.
So, in your case,
function arraycheck () {
var index = arr.indexOf(txtval.value);
if(index > -1){
//alert for found match
}else{
//alert for no match found
}
}

IndexOf Javascript alternative

I'm trying to do an indexof function. but we're not allowed to use it, or any build-in function in javascript. so i create my own. I upload it in our tester but there's only one condition that I didn't met. Thanks!
here's my code :
function indexofmyversion(searchChar,index) {
var a="";
for(var i=0; i<=searchChar.length; i++){
if(searchChar[i] == index){
return i;
}
} return -1; }
The test failed due to fact that the searching should start from index 4 , simply add another parameter as required
function indexofmyversion(searchChar,index, fromIndex) {
var a="";
for(var i=fromIndex; i<searchChar.length; i++){
if(searchChar[i] == index){
return i;
}
} return -1; }

Javascript checking whether string is in either of two arrays

I'm pulling my hair out over this one. I have two arrays, likes & dislikes, both filled with about 50 strings each.
I also have a JSON object, data.results, which contains about 50 objects, each with an _id parameter.
I'm trying to check find all the objects within data.results that aren't in both likes and dislikes.
Here's my code at present:
var newResults = []
for(var i = 0; i<data.results.length; i++){
for(var x = 0; x<likes.length; x++){
if(!(data.results[i]._id == likes[x])){
for(var y = 0; y<dislikes.length; y++){
if(!(data.results[i]._id == dislikes[y])){
newResults.push(data.results[i]);
console.log("pushed " + data.results[i]._id);
}
else
{
console.log("They already HATE " + data.results[i]._id + " foo!"); //temp
}
}
}
else
{
console.log(data.results[i]._id + " is already liked!"); //temp
}
}
}
As you can see, I'm iterating through all the data.results objects. Then I check whether their _id is in likes. If it isn't, I check whether it's in dislikes. Then if it still isn't, I push it to newResults.
As you might expect by looking at it, this code currently pushes the result into my array once for each iteration, so i end up with a massive array of like 600 objects.
What's the good, simple way to achieve this?
for (var i = 0; i < data.results.length; i++) {
isInLiked = (likes.indexOf(data.results[i]) > -1);
isInHated = (dislikes.indexOf(data.results[i]) > -1);
if (!isInLiked && !isInHated) {
etc...
}
}
When checking whether an Array contains an element, Array.prototype.indexOf (which is ECMAScript 5, but shimmable for older browsers), comes in handy.
Even more when combined with the bitwise NOT operator ~ and a cast to a Boolean !
Lets take a look how this could work.
Array.prototype.indexOf returns -1 if an Element is not found.
Applying a ~ to -1 gives us 0, applying an ! to a 0 gives us true.
So !~[...].indexOf (var) gives us a Boolean represantation, of whether an Element is NOT in an Array. The other way round !!~[...].indexOf (var) would yield true if an Element was found.
Let's wrap this logic in a contains function, to simply reuse it.
function contains (array,element) {
return !!~array.indexOf (element);
}
Now we only need an logical AND && to combine the output, of your 2 arrays, passed to the contains function.
var likes = ["a","b","f"] //your likes
var dislikes = ["c","g","h"] //your dislikes
var result = ["a","c","d","e","f"]; //the result containing the strings
var newresult = []; //the new result you want the strings which are NOT in likes or dislikes, being pushed to
for (var i = 0,j;j=result[i++];) //iterate over the results array
if (!contains(likes,j) && !contains (dislikes,j)) //check if it is NOT in likes AND NOT in dislikes
newresult.push (j) //if so, push it to the newresult array.
console.log (newresult) // ["d","e"]
Here is a Fiddle
Edit notes:
1. Added an contains function, as #Scott suggested
Use likes.indexOf(data.results[i]._id) and dislikes.indexOf(data.results[i]._id).
if (likes.indexOf(data.results[i]._id) != -1)
{
// they like it :D
}
Try first creating an array of common strings between likes and dislikes
var commonStrAry=[];
for(var i = 0; i<likes.length; i++){
for(var j=0; j<dislikes.length; j++){
if(likes[i] === dislikes[j]){
commonStrAry.push(likes[i] );
}
}
}
then you can use this to check against data.results and just remove the elements that don't match.

Javascript array search

Whats the best way to search a javascript array for an entry??
All the items will be strings.
Is it simply by using lastIndexOf? like so:
var list= [];
list.push("one");
list.push("two");
list.push("three");
if(list.lastIndexOf(someString) != -1)
{
alert("This is already present in list");
return;
}
Is it simply by using lastIndexOf?
Yes. However, I'd use even simpler indexOf() if you don't need to explicitly search backwards (which you don't if you test for "does not contain"). Also notice that these methods were standardized in ES5 and need to be shimmed in old browsers that do not support them natively.
For older browser support, you should still use a loop:
function inArray(arrToSearch, value) {
for (var i=0; i < arrToSearch.length; i++) {
if (arrToSearch[i] === value) {
return true;
}
}
return false;
};
You can try the build in array method of javascript find. It's the easiest way for your problem. Here is the code:
var list = [], input = "two";
list.push("one");
list.push("two");
list.push("three");
function match(element){
return element == input;
}
if(list.find(match)){
console.log('Match found');
}
var arr = ["one","two","three"];
Array.prototype.find = function(val){
for(var i = 0; i < this.length; i++) {
if(this[i] === val){
alert("found");
return;
}
}
alert("not found");
}
arr.find("two");
Should work in most older browsers.
https://jsfiddle.net/t73e24cp/

JavaScript/jQuery equivalent of LINQ Any()

Is there an equivalent of IEnumerable.Any(Predicate<T>) in JavaScript or jQuery?
I am validating a list of items, and want to break early if error is detected. I could do it using $.each, but I need to use an external flag to see if the item was actually found:
var found = false;
$.each(array, function(i) {
if (notValid(array[i])) {
found = true;
}
return !found;
});
What would be a better way? I don't like using plain for with JavaScript arrays because it iterates over all of its members, not just values.
These days you could actually use Array.prototype.some (specced in ES5) to get the same effect:
array.some(function(item) {
return notValid(item);
});
You could use variant of jQuery is function which accepts a predicate:
$(array).is(function(index) {
return notValid(this);
});
Xion's answer is correct. To expand upon his answer:
jQuery's .is(function) has the same behavior as .NET's IEnumerable.Any(Predicate<T>).
From http://docs.jquery.com/is:
Checks the current selection against an expression and returns true, if at least one element of the selection fits the given expression.
You should use an ordinary for loop (not for ... in), which will only loop through array elements.
You might use array.filter (IE 9+ see link below for more detail)
[].filter(function(){ return true|false ;}).length > 0;
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
I would suggest that you try the JavaScript for in loop. However, be aware that the syntax is quite different than what you get with a .net IEnumerable. Here is a small illustrative code sample.
var names = ['Alice','Bob','Charlie','David'];
for (x in names)
{
var name = names[x];
alert('Hello, ' + name);
}
var cards = { HoleCard: 'Ace of Spades', VisibleCard='Five of Hearts' };
for (x in cards)
{
var position = x;
var card = card[x];
alert('I have a card: ' + position + ': ' + card);
}
I suggest you to use the $.grep() method. It's very close to IEnumerable.Any(Predicate<T>):
$.grep(array, function(n, i) {
return (n == 5);
});
Here a working sample to you: http://jsfiddle.net/ErickPetru/BYjcu/.
2021 Update
This answer was posted more than 10 years ago, so it's important to highlight that:
When it was published, it was a solution that made total sense, since there was nothing native to JavaScript to solve this problem with a single function call at that time;
The original question has the jQuery tag, so a jQuery-based answer is not only expected, it's a must. Down voting because of that doesn't makes sense at all.
JavaScript world evolved a lot since then, so if you aren't stuck with jQuery, please use a more updated solution! This one is here for historical purposes, and to be kept as reference for old needs that maybe someone still find useful when working with legacy code.
Necromancing.
If you cannot use array.some, you can create your own function in TypeScript:
interface selectorCallback_t<TSource>
{
(item: TSource): boolean;
}
function Any<TSource>(source: TSource[], predicate: selectorCallback_t<TSource> )
{
if (source == null)
throw new Error("ArgumentNullException: source");
if (predicate == null)
throw new Error("ArgumentNullException: predicate");
for (let i = 0; i < source.length; ++i)
{
if (predicate(source[i]))
return true;
}
return false;
} // End Function Any
Which transpiles down to
function Any(source, predicate)
{
if (source == null)
throw new Error("ArgumentNullException: source");
if (predicate == null)
throw new Error("ArgumentNullException: predicate");
for (var i = 0; i < source.length; ++i)
{
if (predicate(source[i]))
return true;
}
return false;
}
Usage:
var names = ['Alice','Bob','Charlie','David'];
Any(names, x => x === 'Alice');

Categories