I have multiple selects set up as filters to filter (hide/show) DOM elements within a specific section of the page. The sections are set up like this (simplified):
<section id="people">
<div data-filtervals="{"titleLevel":["2"],"titleFunction":["4","13"]}">John Doe</div>
.
.
.
<div data-filtervals="{"titleLevel":["1"],"titleFunction":["2","3","10"]}">Sally Smith</div>
</section>
My filters produce an array like this in console.log(filter):
titleFunction: Array[2]
0: "1"
1: "2"
length: 2
titleLevel: Array[3]
0: "1"
1: "2"
2: "3"
length: 3
I am trying to write a function that will iterate over .each of the #people > div's and if the data-key(s) are not in the filter[key] value(s), hide the divs.
So in the example above, John Doe would be hidden because his data-titleLevel AND data-titleFunction are not included in the filter array. There are overlaps in data attribute values so combining them into a single array is not possible and legacy code prevents me from altering this for the moment.
My challenge is more about creating a Javascript (or jQuery) function that can dynamically compare one or more filters against data attributes with matching keys. I have been able to get the filter to work for either/or but not to dynamically filter against multiple filters, unless I hard code it.
I looked into .some() but keep hitting a wall with the logic and other SO "Array in Array" solutions like this and this but I have found they don't seem to quite solve this challenge.
EDIT:
I have decided to combine the data attributes into a single data attribute with the value being JSON with keys matching the filter keys; perhaps this will make the comparison easier. Still looking for a solution while I work on it.
Well here's my attempt at this.
I don't know where your filters are coming from but if they're dynamic I hope they are in an one object. With that hypothesis I made a solution.I'm also guessing that you have found a way to organize the divs into one array of objects
So assumming that
var filters = {titleLevel:[1,2],titleFunction:[1,2,3]};
var divs = [{titleLevel:[2],titleFunction:[4,13]},{titleLevel:[1],titleFunction:[2,3,10]}];
var hasOverLap = function(arr1,arr2){
var flag = false;
arr1.forEach((item)=>{
if(arr2.indexOf(item) != -1){flag = true;}
});
return flag;
};
The following code should get you an object of filtered divs
divs.filter(div=>{
for(var key in div){
if(!hasOverLap(div[key],filters[key])){
return false;
}
}
return true;
});
It's not a complete solution but i hope it helps you get started.
I try to add elements in a particular way to the following JSON:
var data = [{"name":"google",
"ip":"10.10.10.01",
"markets":[{"name":"spain","county":"6002,6017,6018,6019,6020"},
{"name":"france","county":"6003,6005,6006,6007,6008,6025,6026,6027,6028,6029"},
{"name":"japan","county":"6004,6021,6022,6023,6024"},
{"name":"korea","county":"6000,6013,6014,6015,6016"},
{"name":"vietnam","county":"6001,6009,6010,6011,6012"}]},
{"name":"amazon",
"ip":"10.10.10.02",
"markets":[{"name":"usa","county":"10000,10001,10002,10003,10004,10005"}]},
{"name":"yahoo",
"ip":"10.10.10.03",
"markets":[{"name":"japan","county":"10000"}]}];
I want to add this element to the json:
newData = [{"name":"amazon",
"ip":"10.10.10.02",
"markets":[{"name":"mexico","county":"9000"}]}];
The result might be exactly this:
var data = [{"name":"google",
"ip":"10.10.10.01",
"markets":[{"name":"spain","county":"6002,6017,6018,6019,6020"},
{"name":"france","county":"6003,6005,6006,6007,6008,6025,6026,6027,6028,6029"},
{"name":"japan","county":"6004,6021,6022,6023,6024"},
{"name":"korea","county":"6000,6013,6014,6015,6016"},
{"name":"vietnam","county":"6001,6009,6010,6011,6012"}]},
{"name":"amazon",
"ip":"10.10.10.02",
"markets":[{"name":"usa","county":"10000,10001,10002,10003,10004,10005"},
{"name":"mexico","county":"9000"}]},
{"name":"yahoo",
"ip":"10.10.10.03",
"markets":[{"name":"japan","county":"10000"}]}];
I tried to use :
$.extend(data.markets, newData)
$.extend(true, data, newData); //this works only in the case every element is new.
but nothing works the way I pretend.
Could anyone give me a solution?
Thanks in advance.
You haven't created JSON, you've created a JavaScript literal object.
You could add this particular piece of newdata by
data[1].markets.push({"name":"mexico","county":"9000"})
Because you are dealing with javascript objects, you can write a function to check for the existence of data[n] and push data.
You have an array of objects, where each object is like the following:
var item = {"name":"...",
"ip":"...",
"markets":[ /*some objects here*/];
}
So why not just creating your custom method to insert elements? It could search in the array if an item with the same name and ip exists, and then:
If it does exist: append the markets to the existing item markets attribute (maybe you need to check again if they already exist). UPDATE:The code that #jasonscript added in his answer will do the job: once you have found where to add the market, just add it to the array. Again, maybe you'll have to check if that market was already in the array. Using jQuery it will be: $.extend(true, data[i],newData)
If it doesn't exist: just add the item to the array: $.extend(true, data,newData)
Stealing a little code from another answer:
$.each(data, function(item){
if(item.name == newData[0].name && item.ip == newData[0].ip) {
item.markets.push.apply(item.markets, newData[0].markets);
}
}
This assumes that you know that all the market items in the new object are different to the existing ones - otherwise you'd have to do a nested foreach or something. If you can change the notation of the objects a little you could think about using a dictionary-like object for Markets to make that a little cleaner.
In fact, changing data from an associative array would probably work for that too. Then you could easily check for existence with:
if(data[myNewDataName]){
//add to markets
} else {
data[myNewDataName] = myNewData;
}
Assuming I have a collection with a bunch of well ordered elements, what's the common method to insert another new child at it's abstract order-position?
Using a dom library $(new).eq($(new).data('orderPosition')); doesn't work, because it's not a valid index.
// Add this element to it's logic position in the collection:
<div data-order-position="10"></div>
// The collection
<div id="myCollection">
<div data-order-position="4"></div>
<div data-order-position="67"></div>
<div data-order-position="81"></div>
<div data-order-position="82"></div>
<div data-order-position="761"></div>
</div>
My real collection contains about ~400 elements.
I think that working with an array of integers is probably the most efficient method. You can maintain a constant list of the sorted elements in an array somewhere (and even continue to sort as needed):
//Array of positions
var positions = [];
//Initially set up the array in question
//divs are already sorted, as we know
$("#myCollection div").each(function () {
positions.push(parseInt(this.dataset.orderPosition));
});
//Create the new node we want to insert (in your case it may already exist)
var $new = $("<div>").data('order-position', 10).text(10);
//Append the new node index (if node already exists, just use `.data` as above)
positions.push(10);
//Yes, for whatever reason JS sorts by string and not number by default.
//There may also be a more efficient way to add the integer in the correct spot
//without having to sort all over again, but this is fast enough
positions.sort(function (a, b) {
return a - b;
});
//Insert the new node!
$("#myCollection div").eq(positions.indexOf(10) - 1).after($new);
http://jsfiddle.net/ExplosionPIlls/ULEFX/
why don't you just store the order-position in an array and then calculate the index using it? it is far better solution as reading DOM property consumes a lot more CPU than just loop through array and compare your new item with existing ones
I have a collection with models that all have an array of tags.
If I wanted to grab a model that had one tag I would do like so:
models.filter(function(x) {
return _.contains(x.get("tags"), 'google');
});
This would grab the models that have the tag google. The problem I am having is that I cant put more that one tag there, I need for it to be an array. How would I go about grabbing all the models that have multiple tags (ex: google, yahoo, apple)? The contains method only supports 1 string and not an array
You could use _.intersection thusly:
var want_all = ['google', 'yahoo', 'apple'];
models.filter(function(m) {
return _.intersection(m.get('tags'), want_all).length == want_all.length;
});
The _.intersection(a, b) will give you an array with all the elements that are common to both a and b, if the intersection is the same size as the array/set you're looking for then m.get('tags') has everything you're looking for.
I have some JSON which looks generally like this...
{"appJSON": [
{
"title":"Application Title",
"category":"Business",
"industry":"Retail",
"language":"English",
"tags":[
{"tags":"Sales"},{"tags":"Reporting"},{"tags":"Transportation"},{"tags":"Hospitality"}
],
},
{
"title":"Airline Quality Assurance",
...
...
...]}
I'm looping through JSON to get an array of all of the unique Tags in the data.
My question is, now that I have an array of the different unique Tags in the JSON, how do I best determine the number of times each Tag occurs?
So basically I'm looking to generate a list of all of the tags found in the JSON (which I already have) with the number of times each one occurs (which I don't already have).
Thanks a lot in advance!
I'm assuming when you find a new tag you check to see if you already have that tag somewhere. If you don't you add it to your list. Why not when you check do something like.
var nextTag=//get the next tag in the JSON list
var newTag=true;
for(var i=0;i<tags.length;i++){
if(nextTag === tags[i]){
tagCount[i]++;
newTag=false;
break;
}
}
if(newTag){
tags[tags.length]=nextTag;
tagCount[tagCount.length]=1;
}
This uses two arrays where tagCount[i] is the number of times tag in tags[i] occurs. You could uses an object to do this or however you wanted to.
As an alternative, here's a function which will fill an associative array; the keys will be the tags and the values will be the number of occurrences of that tag.
var tagCounts = []; // Global variable here, but could be an object property or any array you like really
function countTags(tags, tagCounts)
{
$.each(tags, function(i, item) {
var tag = item.tags; // This would change depending on the format of your JSON
if(tagCounts[tag] == undefined) // If there's not an index for this tag
tagCounts[tag] = 0;
tagCounts[tag]++;
});
}
So you can call this function on any number of arrays of tags, passing in your tagCounts (totals) array, and it will aggregate the totals.
var tags1 = [{"tags":"Sales"},{"tags":"Reporting"},{"tags":"Transportation"},{"tags":"Hospitality"}];
var tags2 = [{"tags":"Reporting"},{"tags":"Transportation"}];
var tags3 = [{"tags":"Reporting"},{"tags":"Hospitality"}];
countTags(tags1, tagCounts);
countTags(tags2, tagCounts);
countTags(tags3, tagCounts);
Then you can read them out like so:
for(var t in tagCounts)
// t will be the tag, tagCounts[t] will be the number of occurrences
Working example here: http://jsfiddle.net/UVUrJ/1/
qw3n's answer is actually a more efficient way of doing things, as you're only looping through all the tags onceābut unless you have a really huge JSON source the difference isn't going to be noticeable.