views:

184

answers:

6

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

A: 

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");
Mark Byers
Should be `var f` and `var g`
Justin Johnson
Fixed, thanks. This was an error in the original code. I tried to fix the code without making too many changes, but I guess I could have fixed that error too.
Mark Byers
A: 

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");
AnthonyWJones
+2  A: 
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)

bobince
A: 

Thanks for your replies guys! I really appreciate all of you taking the time to respond, for the sake of closing the question I'll just accept the answer which was chronologically first.

Cheers :-)

Deacon
Thanks for choosing my answer. I was definitely the first and probably a catalyst for other people seeing my answer and wanting to improve upon it, so I think it's fair I get some credit. I also tried to produce something that would fit in with your coding style, not necessarily the most perfectly elegant answer. However you ought to choose the best answer, not the first one, nor the one that deserves the most credit. This will most help other users who search here.
Mark Byers
+1  A: 

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.

Nosredna
A: 

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 );
Korneel Bouman