3

How to loop through list of maps to filter out SearchMap key-values from below List having map of records using JS?

Map

var searchMap = new Map()
searchMap.set("ed_mood", "strong")
searchMap.set("ed_target_audience", "Expert")
searchMap.set("ed_clip_type", "intro")

List

var master_data =
[
    {ed_mood: "Light", ed_rating: 10, ed_target_audience: "Novice", ed_clip_type: "Basic"},
    {ed_mood: "Light", ed_rating: 5, ed_target_audience: "Expert", ed_clip_type: "Q&A"},
    {ed_mood: "Strong", ed_rating: 8, ed_target_audience: "Expert", ed_clip_type: "Intro"},
    {ed_mood: "Strong", ed_rating: 7, ed_target_audience: "Expert", ed_clip_type: "Q&A"},
    {ed_mood: "Strong", ed_rating: 10, ed_target_audience: "Expert", ed_clip_type: "intro"}
]

Note: To filter out record I am using AlaSql but it doesn't give expected result. Any other JS way to filter map to list of maps?

var filter_result = [];
searchMap.forEach(function(value, key){
    var data  = alasql(`select * from ? where ${key} like ?`,[master_data, `%${value}%`]);
    $.each(data, (i) => filter_result.push(data[i]));
});

Expected Result

[
   {ed_mood: "Strong", ed_rating: 8, ed_target_audience: "Expert", ed_clip_type: "Intro"},
   {ed_mood: "Strong", ed_rating: 10, ed_target_audience: "Expert", ed_clip_type: "intro"}
]

3 Answers 3

2

The following code filters master_data to only return Objects that match every param in searchMap.

See Array.prototype.filter(), Array.prototype.every(), Map.entries(), JSON.stringify() and String.toLowercase() for more info.

// Search Map.
const searchMap = new Map([
  ['ed_mood', 'strong'],
  ['ed_target_audience', 'Expert'],
  ['ed_clip_type', 'intro']
])

// Master Data.
const master_data = [
  {ed_mood: 'Light', ed_rating: 10, ed_target_audience: 'Novice', ed_clip_type: 'Basic'},
  {ed_mood: 'Light', ed_rating: 5, ed_target_audience: 'Expert', ed_clip_type: 'Q&A'},
  {ed_mood: 'Strong', ed_rating: 8, ed_target_audience: 'Expert', ed_clip_type: 'Intro'},
  {ed_mood: 'Strong', ed_rating: 7, ed_target_audience: 'Expert', ed_clip_type: 'Q&A'},
  {ed_mood: 'Strong', ed_rating: 10, ed_target_audience: 'Expert', ed_clip_type: 'intro'}
]

// Output.
const output = 
  
  // Filter master_data for Objects that include every searchpoint.
  master_data.filter((datapoint) =>
   
  // Destructuring assignment + Map.entries to reveal searchMap entries.
  [...searchMap.entries()].every((searchpoint) =>
  
  // Object.entries() to reveal datapoint entries. 
  // JSON.stringify + toLowerCase() for normalization.
  JSON.stringify(Object.entries(datapoint)).toLowerCase().includes(JSON.stringify(searchpoint).toLowerCase())))


// Log.
console.log(output)

Sign up to request clarification or add additional context in comments.

3 Comments

isn't this too much code for very little work. I think in such a case, expressive code would do good then desriptive. Although for the understanding part, good to go! Also, this is too much computation, stringifying every entry and using String.includes to check if the value of the entry is present, that is an *overkill•.
The code was originally 1 line. I split it into 3 and added comments to help the asker understand what's going on. JSON.stringify() cheaply normalises the data for efficient comparison. Please feel free to post a more terse / efficient solution if you believe one exists 😁
I'd rather not stringify. I'd make a searchMatchers = Object.keys(searchMap).map(function(data){ return [data,searchMap[data]]; }); Then, const endResult = master_data.filter(dataObj => { return searchMatchers.every(([key, value]) => { return dataObj[key] === value; }) })
1

The problem in your code is that you are concatenating the conditions using (implicitly) the OR operator. Your expected result suggests that you should use the AND operator.

You could build your SQL command first and then execute it using AlaSQL (just a suggestion, you introduced me to AlaSQL :-) )

var searchMap = new Map();
searchMap.set("ed_mood", "strong");
searchMap.set("ed_target_audience", "Expert");
searchMap.set("ed_clip_type", "intro");

var master_data =
[
    {ed_mood: "Light", ed_rating: 10, ed_target_audience: "Novice", ed_clip_type: "Basic"},
    {ed_mood: "Light", ed_rating: 5, ed_target_audience: "Expert", ed_clip_type: "Q&A"},
    {ed_mood: "Strong", ed_rating: 8, ed_target_audience: "Expert", ed_clip_type: "Intro"},
    {ed_mood: "Strong", ed_rating: 7, ed_target_audience: "Expert", ed_clip_type: "Q&A"},
    {ed_mood: "Strong", ed_rating: 10, ed_target_audience: "Expert", ed_clip_type: "intro"}
];

var command = "SELECT * FROM ? WHERE";
var values = [];

searchMap.forEach(function(value, key){
	command += ` ${key} LIKE ? AND`;
	values.push('%' + value);
});

//Removing the last "AND"
command = command.substring(0, command.length -4);

var filter_result = alasql(command, [master_data, ...values]);

console.log(filter_result);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/alasql/0.4.5/alasql.min.js"></script>


Solution without AlaSQL

Using ES6, your code could be:

var searchMap = new Map();
searchMap.set("ed_mood", "strong");
searchMap.set("ed_target_audience", "Expert");
searchMap.set("ed_clip_type", "intro");

var master_data =
[
    {ed_mood: "Light", ed_rating: 10, ed_target_audience: "Novice", ed_clip_type: "Basic"},
    {ed_mood: "Light", ed_rating: 5, ed_target_audience: "Expert", ed_clip_type: "Q&A"},
    {ed_mood: "Strong", ed_rating: 8, ed_target_audience: "Expert", ed_clip_type: "Intro"},
    {ed_mood: "Strong", ed_rating: 7, ed_target_audience: "Expert", ed_clip_type: "Q&A"},
    {ed_mood: "Strong", ed_rating: 10, ed_target_audience: "Expert", ed_clip_type: "intro"}
];

var filter_result = master_data.filter(function(x) {
    for (var [key, value] of searchMap) {
        //Change the comparison to fit your needs
    
        // Condition to handle null , undefined and ''(blank) values
        if (x[key] !== null && typeof x[key] !== 'undefined' && x[key] !== '') {
            if (x[key].toLowerCase() !== value.toLowerCase()) return false;
        } else {
            return false;
        }
    }
    return true;
});

console.log(filter_result);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/alasql/0.4.5/alasql.min.js"></script>

1 Comment

No need to reference alasql in the second snippet. It currently results in "SecurityError: The operation is insecure." By removing the reference to alasql, there is no error in the output. For the first snippet, alas I have no work-around. Sorry! (It most likely works just fine in jsFiddle.)
0

You can use lodash's filter method here.

In your case, you will do:

const result = _filter(master_data, {
  ed_mood: 'strong',
  ed_target_audience: 'expert',
  ed_clip_type: 'intro'
});

Read more about it on lodash/filter

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.