Deacon
Deacon

Reputation: 33

Getting javascript to search an array within an array

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

Upvotes: 1

Views: 395

Answers (5)

bobince
bobince

Reputation: 536695

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)

Upvotes: 2

Korneel Bouman
Korneel Bouman

Reputation: 201

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

Upvotes: 0

Mark Byers
Mark Byers

Reputation: 839074

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");

Upvotes: 0

Nosredna
Nosredna

Reputation: 86336

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.

Upvotes: 1

AnthonyWJones
AnthonyWJones

Reputation: 189535

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");

Upvotes: 0

Related Questions