0

I've looked at a number of other posts and can't resolve my case.

I am working on a music/chord playing program and I want to filter out all chords (aka objects) that include one or more notes (numbers in my program) that are out of key.

I have an array named chordLibrary filled with objects (e.g., {notesInChord: [5, 8], chordName: 'Major'}). I have another array with numbers (outOfKeyNotes = [11, 12];). I want to .filter and return only the objects in chordLibrary that do not include the numbers in outOfKeyNotes in the element notesInChord.

For example:

// THIS is the original array:
const chordLibrary = [ 
    { notesInChord: [5, 8], chordName: 'Major' },
    { notesInChord: [5, 8, 11], chordName: 'Dominant 7th' },
    { notesInChord: [4, 8, 12], chordName: 'Minor Major 7th' }
];

// THIS is what I hope to end up with after filtering for the array [11,12]:
let chordsInKey = [ 
    { notesInChord: [5, 8], chordName: 'Major' },
];

Here is my program which is currently not working. it simply returns the whole original array.

const chordLibrary = [ 
    { notesInChord: [5, 8], chordName: 'Major' },
    { notesInChord: [5, 8, 11], chordName: 'Dominant 7th' },
    { notesInChord: [4, 8, 12], chordName: 'Minor Major 7th' }
];

let outOfKeyNotes = [11, 12];

console.log(chordLibrary.length);

let chordsInKey = chordLibrary.filter(function(item) {
    return !outOfKeyNotes.includes(item.notesInChord);
});

console.log(chordsInKey);
console.log(chordsInKey.length);

If I change the program so that chordLibrary is simply an array of number values instead of an array of objects, it works fine. It just doesn't suit my needs. Here is a working example of that:

let chordLibrary = [1,2,3,11,12];
let outOfKeyNotes = [11, 12];

console.log(chordLibrary.length);

let chordsInKey = chordLibrary.filter(function(item) {
    return !outOfKeyNotes.includes(item);
});

console.log(chordsInKey);
console.log(chordsInKey.length);

What am I missing? Thanks

tkore
  • 1,101
  • 12
  • 31
forestkelley
  • 341
  • 3
  • 14

2 Answers2

2

You're trying to use includes to see if an array of values includes another array of values.

Instead, you can use Array.every in your filtering mechanism, which makes sure every element in an array passes a specific test.

I would change

return !outOfKeyNotes.includes(item.notesInChord);

to

return item.notesInChord.every(note => !outOfKeyNotes.includes(note));
tkore
  • 1,101
  • 12
  • 31
1

You can use Array#some() to check if any of the notesInChord values exist in outOfKeyNotes

const chordLibrary = [ 
    { notesInChord: [5, 8], chordName: 'Major' },
    { notesInChord: [5, 8, 11], chordName: 'Dominant 7th' },
    { notesInChord: [4, 8, 12], chordName: 'Minor Major 7th' }
];

let outOfKeyNotes = [11, 12];

let chordsInKey = chordLibrary.filter(function(item) {
    return !item.notesInChord.some(function(v){
         return outOfKeyNotes.includes(v);
    });
});

console.log(chordsInKey);
charlietfl
  • 170,828
  • 13
  • 121
  • 150
  • I've learned that .some would normally return any element that does include one of the values that exist. However, here it is returning only the one chord that does not include outOfKeyNotes. I assume that the use of "!" is making it work that way? @TalKoren's answer seems to be a cleaner version of this but they work the same way, yeah? – forestkelley Sep 01 '19 at 16:56
  • Correct. `some()` returns a boolean and you want to negate it (using `!`) since you don't want any that are included – charlietfl Sep 01 '19 at 16:58
  • The other isn't any cleaner...I could have written this using arrow functions also but kept it as es5 function syntax to be consistent with how you were writing your code. Note that `some()` breaks as soon as a match is encountered whereas `every()` has to go through whole array. On small array it's not really significant....on large data set can make a difference – charlietfl Sep 01 '19 at 16:59
  • Ah, very interesting. Thanks so much for the clarification. I'm excited to use .some as well. Very useful. – forestkelley Sep 01 '19 at 17:15