Jquery object search - javascript

I have the following object and what I would like achieve is to get the index of theme if the name has match with a variable.
for example: I'm making a loop in the views and if my task (something1) variable has matches with the name element than to return the index of object.
By the given example I should have as result 0,
var views = [
{
name: "something1",
type: something1,
columns: something1
},
{
name: "something2",
type: something2,
columns: something2
},
{
name: "something3",
type: something3,
columns: something3
}
];
var task = 'something1';
$.each(views, function(index, value) {
if (value.name = task) {
alert(index);
}
});

You dont really need jQuery for this:
See: http://jsfiddle.net/enNya/2/
var views = [
{
name: "something1",
type: "something1",
columns: "something1"
},
{
name: "something2",
type: "something2",
columns: "something2"
}
];
var task = 'something2';
// Set a var and maintain scope
var i;
// Loop each element of the array
for (i = 0; i < views.length; i++) {
// If the X = Y the stop looping
if (views[i].name == task) {
break;
}
}
// Check if it was not found
i = i == views.length ? false : i;
// Log the result
console.log(i);

It's just a matter of syntax, as lgt said don't forget toseparate elements within your object with commas. Aslo the correct 'equal' operator is '=='.
'value.name=task' would be always true. It means can I affect the content of 'task' into 'value.name'.
Here is your valid js.
Note that in this example you'll get 2 alertbox.. ;)
var views=[
{
name:"something1",
type:'something1',
columns:'something1'
},
{
name:"something1",
type:'something1',
columns:'something1'
},
{
name:"something2",
type:'something2',
columns:'something2',
},
];
var task='something1';
$.each(views, function(index, value) {
if (value.name==task){
alert(index);
}
});

replace something1 variable for 0 and value.name == task (double =)
var views=[{
name:"something1",
type:0,
columns:0
}, {
name:"something1",
type:0,
columns:0
}, {
name:"something2",
type:0,
columns:0
}];
var task='something1';
$.each(views, function(index, value) {
if (value.name==task){
return index;
}
});

Related

Recursion in JavaScript to search in heavy JSON

I am facing an algorithmic conception problem. With JavaScript language, I have an heavy JSON object of about 11 000 lines, that is the result of the conversion of an HTML file. The structure of the JSON is similar to the one of the DOM, which means that an Object can have a property children, a data structure composed of other similar Object. The goal is to search in the JSON and extract the information of the property itemprop of the Object that has that property. The itemprop attribute is in and Object inside the attributes attribute that some of the first mentioned Object have.
Object Structure
{ type: 'x',
tagName: 'y',
attributes: { "itemprop" : "valueWanted" },
children:
[ Object, Object, Object]
}
I thought of a recursive algorithm for solution. Unfortunately, I am not familiar with recursion and the next code is not working.
Recursive Algorithm
var searchAttributesRecursive = function(children) {
for (var i = 0; i < children.length; ++i) {
if (children[i].hasOwnProperty('children')) {
return searchAttributesRecursive(children[i].children);
}
else {
if (children[i].hasOwnProperty('attributes')) {
if (children[i].attributes.itemprop === "valueWanted") {
console.log('success')
}
}
}
return; // probably a problem that breaks the loop
}
};
searchAttributesRecursive(startingChildren);
There is maybe another more effective generic algorithms to get this task done. I am open to suggestions.
Update
Thank you for all solutions and explanation provided. More particularly, have a look to #ChrisG's simple solution. Now, I would like to add a special condition in the algorithm.
If I would like to retrieve the data from the next object, outside of the scope of the children where an object has the wantedValue2, do you have an idea how I can access this data? The algorithm would have a special case where it meets wantedValue2, and don't want to extract directly the data of itemprop.
Object Structure Special Case
{
"type": "",
"tagName": "",
"attributes": {
"itemprop": "wantedValue"
},
"children": [{
"type": "",
"content": ""
}
]
},
{
"type": "",
"content": ""
}]
},
{
"type": "",
"tagName": "",
"attributes": {},
"children": [
{
"type": "",
"content": "here"
}
]
Here's a shorter version:
Note that the function expects an array, so if your object is not an array, you have to use findItemprop([dom], "wanted")
function findItemprop(data, value, found) {
if (!found) found = [];
data.forEach((node) => {
if (node.attributes && node.attributes.itemprop == value)
found.push(node);
if (node.children) findItemprop(node.children, value, found);
});
return found;
}
var dom = [{
tag: "root",
children: [{
tag: "header",
children: [{
tag: "div"
}]
}, {
tag: "div",
id: "main",
children: [{
tag: "p",
attributes: {
itemprop: "wanted"
}
}]
}, {
tag: "footer",
children: [{
tag: "span",
content: "copyright 2017",
attributes: {
itemprop: "wanted"
}
}]
}]
}];
console.log(findItemprop(dom, "wanted"));
Your return will break the loop. You just want to return if it does return:
var searchAttributesRecursive = function(children) {
for (var i = 0; i < children.length; ++i) {
if (children[i].hasOwnProperty('children')) {
var result=searchAttributesRecursive(children[i].children);
if(result) return result;//if weve found sth, return
}
if (children[i].hasOwnProperty('attributes')) {
if (children[i].attributes.itemprop === "valueWanted1") {
console.log('success')
return children[i];//return sth useful
}
}
}
return false;//nothing found in this and in all childs
};
var elem=searchAttributesRecursive(startingChildren);
This returns the first found child. You may want to return an array instead:
var searchAttributesRecursive = function(children,result=[]) {
for (var i = 0; i < children.length; ++i) {
if (children[i].hasOwnProperty('children')) {
searchAttributesRecursive(children[i].children,result);
}
if (children[i].hasOwnProperty('attributes')) {
if (children[i].attributes.itemprop === "valueWanted1") {
console.log('success')
result.push(children[i]);//return sth useful
}
}
}
return result;//return all results found
};
var arr=searchAttributesRecursive(allElems);
arr.forEach(console.log);
Through passing an array as optional parameter it is fast and easy to store the traversal of multiple trees in one result:
var arr=[];
searchAttributesRecursive(allElems,arr);
searchAttributesRecursive(allElemsTwo,arr);
Give Jonas w the credit for their answer, I'm just tagging on to help correct some of the confusion surrounding the recursion, and hopefully make the it a little easier to understand and work with.
First, you're passing in the array of children. That's fine, but then you have to access each one from its array index as you check them. My recommendation is to make your function handle only one item at a time. (I'm going to use Jonas w's method of collecting nodes, because there may be more than one node with this attribute. I'm also going to add the attribute name as a parameter to make it a little more dynamic.)
function searchAttributesRecursive(currentNode, parameterName, results=[]){
}
Now you can concentrate on one and only one node at a time. Once it has passed the check, you can move on to the children.
function searchAttributesRecursive(currentNode, parameterName, results=[]){
if(currentNode.attributes && currentNode.attributes[parameterName]){
results.push(currentNode);
}
if(currentNode.children){
for(var i = 0, len = currentNode.children.length; i < len; ++i){
searchAttributesRecursive(currentNode.children[i], parameterName, results);
}
}
}
Calling it like this:
var results = [];
searchAttributesRecursive(yourJsObject, "itemprop", results);
...populates results with nodes which contain the "itemprop" attribute. You can now print the attribute values with a simple loop:
for(var i = 0, len = results.length; i < len; ++i){
console.log(i, results[i].attributes.itemprop);
}
You can do this by using the .some() function. What this does is it will return true if any iteration returns true, otherwise it returns false. So, for every key in the current object, you will check if the property is === 'attributes', and if so, you will check the itemprop property for the desired value. If the current key is not 'attributes', and is === 'children' it will recurse and check each child object in the same way.
var searchAttributesRecursive = function(obj, valueWanted) {
if (obj instanceof Object) {
if (obj.attributes && obj.attributes.itemprop === valueWanted) {
return true;
}
if (obj.children) {
return obj.children.some(function(_obj) {
return searchAttributesRecursive(_obj, valueWanted);
});
} else {
return false;
}
} else {
return false;
}
};
var obj = {
type: 'x',
tagName: 'y',
attributes: {
"itemprop": "wantedValue0"
},
children: [{
type: 'x',
tagName: 'y',
attributes: {
"itemprop": "wantedValue1"
},
children: []
},
{
type: 'x',
tagName: 'y',
attributes: {
"itemprop": "wantedValue2"
},
children: [{
type: 'x',
tagName: 'y',
attributes: {
"itemprop": "wantedValue3"
},
children: []
}]
}
]
};
console.log("Found 'wantedValue0': " + searchAttributesRecursive(obj, "wantedValue0"));
console.log("Found 'wantedValue1': " + searchAttributesRecursive(obj, "wantedValue1"));
console.log("Found 'wantedValue2': " + searchAttributesRecursive(obj, "wantedValue2"));
console.log("Found 'wantedValue3': " + searchAttributesRecursive(obj, "wantedValue3"));
console.log("Found 'wantedValue4': " + searchAttributesRecursive(obj, "wantedValue4"));
EDIT - To make this work dynamically and search for itemprop === wantedValue in any nested property or nested child property, you can do the following:
var searchAttributesRecursive2 = function(data, valueWanted) {
if (Array.isArray(data)) {
return data.some(function(elem) {
return searchAttributesRecursive2(elem, valueWanted);
});
} else if (data instanceof Object) {
return Object.keys(data).some(function(key) {
var prop = data[key];
return prop.itemprop === valueWanted || searchAttributesRecursive2(prop, valueWanted);
});
} else {
return false;
}
};
var obj = {
type: 'x',
tagName: 'y',
attributes: {
"itemprop": "wantedValue0"
},
children: [{
type: 'x',
tagName: 'y',
attributes: {
"itemprop": "wantedValue1"
},
children: []
},
{
type: 'x',
tagName: 'y',
attributes: {
"itemprop": "wantedValue2"
},
children: [{
type: 'x',
tagName: 'y',
attributes: {
"itemprop": "wantedValue3"
},
children: []
}]
}
]
};
console.log("Found 'wantedValue0': " + searchAttributesRecursive2(obj, "wantedValue0"));
console.log("Found 'wantedValue1': " + searchAttributesRecursive2(obj, "wantedValue1"));
console.log("Found 'wantedValue2': " + searchAttributesRecursive2(obj, "wantedValue2"));
console.log("Found 'wantedValue3': " + searchAttributesRecursive2(obj, "wantedValue3"));
console.log("Found 'wantedValue4': " + searchAttributesRecursive2(obj, "wantedValue4"));

Manipulating array in javascript by slicing

I have an array of twelve items :
var arrToIterate = [];
arrToIterate = //Data from service to populate;
Each item in the array has two fields : Name and title.
If suppose the array has Names as :
Scenario 1:
[0]: Name: Iron
[1]: Name: Steel
[2]: Name: ContainsIron
[3]: Name: ContainsSteel
[4]: Name : Manganese
[5]: Name: Magnesium
I need the ouput as :
[0]: Name: Iron
[1]: Name: Steel
[2]: Name : Manganese
[3]: Name: Magnesium
Scenario 2:
If suppose the array has Names as :
[0]: Name: Iron
[1]: Name: Steel
[2]: Name : Manganese
[3]: Name: Magnesium
I need the output as:
[0]: Name: Manganese
[1]: Name: Magnesium
I am trying to do in this way :
$.each(arrToIterate, function (element, index, arr) {
if (element.Name == "ContainsIron" || element.Name == "ContainsSteel") {
arr.splice(index, 1);
}
});
But I am not getting the handle the second scenario . How do I achieve array manipulation for both the scenarios?
Edit :
If the array contains "ContainsIron" , then ContainsIron needs to be removed ,similarly if it contains ContainsSteel , then ContainsSteel needs to be removed.
Else
If array doesnt contain ContainsSteel , then Steel needs to be removed .
If array doesnt contain ContainsIron, then Iron needs to be removed .
Here's a simple version. First, you'll want to make a remove function which removes something if it's in there, and a function which removes a named metal:
function removeIfExists(arr,name){
// get the index of the entry:
for(var i in arr){
if(arr[i].Name==name){
// Got it! Rip it out:
arr.splice(i,1);
return true;
}
}
// Not in the array at all.
return false;
}
// Removes a metal from the given array
function removeMetal(arr,name){
// Try removing e.g. ContainsIron:
if(!removeIfExists(arr,"Contains"+name)){
// ContainsIron/ ContainsSteel wasn't in there. Try removing 'Iron'/ 'Steel':
removeIfExists(arr,name);
}
}
That leaves the usage as just:
removeMetal(arrToIterate,"Iron");
removeMetal(arrToIterate,"Steel");
Here it is as a fiddle
You can just use vanilla Javascript to do this, using the filter function:
var arrToIterate = [{
name: 'Iron'
}, {
name: 'Iron'
}, {
name: 'Iron'
}, {
name: 'Manganese'
}, {
name: 'Iron'
}, {
name: 'Iron'
}, {
name: 'Iron'
}, {
name: 'ContainsSteel'
}];
filterArray(arrToIterate);
function filterArray(arrToIterate) {
var containsSteel = false,
containsIron = false,
filteredArray;
arrToIterate.forEach(function(item) {
(item.name === 'ContainsSteel') ? containsSteel = true: null;
(item.name === 'ContainsIron') ? containsIron = true: null;
});
console.log("Original Array");
console.log(arrToIterate);
console.log("ContainsSteel " + containsSteel);
console.log("ContainsIron " + containsIron);
if (containsSteel) {
filteredArray = arrToIterate.filter(function(item) {
return !(item.name === 'ContainsSteel');
});
}
if (containsIron) {
filteredArray = filteredArray.filter(function(item) {
return !(item.name === 'ContainsIron');
});
}
if (!(containsIron)) {
filteredArray = filteredArray.filter(function(item) {
return !(item.name === 'iron');
})
}
if (!(containsSteel)) {
filteredArray = filteredArray.filter(function(item) {
return !(item.name === 'Steel');
})
}
console.log("Filtered array ");
console.log(filteredArray);
return filteredArray;
};
Because of the lack of consistency of the "Contain" rules (Manganese for example, doesn't have them), we need to define a hash of what won't be displayed if there isn't a contain rule for it.
Afterwards we can scan the array for for "Contain" rules update the hash, and then just filter array accordingly.
var arrToIterate = [
{ Name: 'Iron' },
{ Name: 'Steel' },
{ Name: 'ContainsIron' },
{ Name: 'Manganese' },
{ Name: 'Magnesium' }
];
var result = arrToIterate.filter(function(metal) {
return metal.Name in this ? this[metal.Name] : true; // if there's an entry in the hash (this) use it. If not keep the item.
}, arrToIterate.reduce(function(hash, metal) { // create the hash
if(metal.Name.indexOf('Contains') !== -1) { // if there's a contain rule
hash[metal.Name] = false; // remove the rule
hash[metal.Name.replace('Contains', '')] = true; // show the metal
}
return hash;
}, { Iron: false, Steel: false })) // the baseline is false for every metal with a Contain rule
console.log(result);
If you just want to filter the array you can use filter:
var array = [
{ name: 'Iron'},
{ name: 'Steel'},
{ name: 'Manganese'},
{ name: 'Magnesium'}
];
var filtered = array.filter(function(item) {
return item.name === 'Iron';
})
Not sure what your selection criteria is, but you just need to define these in the body of the filter callback.
Edit
I see you updated your criteria - so you would just have to amend the filter callback accordingly as I think other responders have now indicated.

compound index/searching on child array

Based on the data below, I'm looking to do something like "find block 1 where the parent objects name is 'Panel'"
So, I tried setting up a compound index like this:
objStore.createIndex('by_name_and_block', ['Name', 'blocks.Name']);
And then calling it (sort-of) like this:
var index = objStore.index("by_name_and_block");
var request = index.get("Panel", "1");
// I've also tried:
// var request = index.get(["Panel","1"]);
...
But this doesn't work. Is there a way to set up this compound index in indexeddb?
Sample data:
[
{
Name: "Post",
blocks: [
{
Name:"1",
Arrays:[]
},
{
Name:"2",
Arrays:[]
},
]
},
{
Name: "Panel",
blocks: [
{
Name:"1",
Arrays:[]
},
{
Name:"2",
Arrays:[]
},
]
},
]
Your data is not able to index with current specification. See steps for extracting key from keyPath. Notice that object is not valid key value in array key path.
In v2, you will be able to use with index function expression.
Currently, you will have to generate extra variable before you persist into the database and remove it after retrieval. Use multiEntry index without compound index.
Is this script that what you want?
var obj = [
{
Name: "Post",
blocks: [
{
Name:"1",
Arrays:[]
},
{
Name:"2",
Arrays:[]
},
]
},
{
Name: "Panel",
blocks: [
{
Name:"1",
Arrays:[]
},
{
Name:"2",
Arrays:[]
},
]
},
];
function getBlockByName(objName, index){
for(var i = 0; i < obj.length; i++){
if(obj[i].Name == objName)
return obj[i].blocks[index];
}
return false;
}
//Index starting from 0
console.log(getBlockByName("Panel", 1));
//Will return {Name:"2", Arrays:[]} object

Building an recursive tree from another

I have an problem with building an recursive tree from another. The function seems to work but if you expand the first array you'll notice that there is an endless recursion at index 2.
My function which builds the tree:
var buildTree = function(data, idx, aparent){
var parent = aparent || [];
for(var i = 0, c = data.length ; i < c ; i++){
parent.push({
text: data[i].text,
items: []
});
if(typeof data[i].items !== 'undefined' && data[i].items.length > 0){
var t = buildTree(data[i].items, idx + 1, parent[parent.length - 1].items);
parent[parent.length - 1].items.push(t);
}
}
return parent;
};
And that's how my tree data looks like:
[{
text: "house",
groupId: "1",
type: "group",
items: [{
text: "basement",
groupId: "2",
type: "group"
},{
text: "flat-1",
groupId: "3",
type: "group",
items: [{
text: "computer"
}]
}]
},{
text: "other-house",
groupId: "4",
type: "group"
}];
I think i has something to do that javascript returns the value by reference...
Here's a plunk with the complete data, check the console to after clicking the button to get an idea what i mean.
I can't really get my head around your code. Maybe your problem has something to do with the fact that you pass in the items-array during your recursion.
I have fixed up your code - making it a bit more simple and easy to read. It relies on the property items being an array if present, so if that is not always the case you need to add error handling for this scenario.
function recursiveBuildTree(data) {
var result = [];
data.forEach(function(item) {
var newItem = {
text: item.text,
items: item.items ? recursiveBuildTree(item.items) : []
};
result.push(newItem);
});
return result;
}

javascript find an object with specific properties in an array

I need to make an extension to existing code, can't change it.
There's this array:
var availableTags = [
{ label: "Yoga classes", category: "EDUCATIONAL" },
{ label: "Cooking classes", category: "EDUCATIONAL" },
{ label: "Cheese tastings", category: "EDUCATIONAL" },
{ label: "Maker Workshops", category: "PRACTICAL" },
{ label: "Seminars", category: "PRACTICAL" },
//many more of these
];
Now I need to check if a text entered in an input box is included in one of the labels, e.g. if the user enters "Yoga classes" => OK, if "Yoga" => NOK, "sdsdf" => NOK, etc.
What is the best way to do this? I am not sure I can use Array.indexOf as I am not sure how to pass the Object to the function, I would try looping through the array (around 40 entries) and compare each object.
You need to loop over every item in availableTags and check whether that item's label is equal to some input. Try something like this:
var input = "Yoga classes";
var found = false;
for (var i = 0, j = availableTags.length; i < j; i++) {
var cur = availableTags[i];
if (cur.label === input) {
found = true;
break;
}
}
console.log(found);
DEMO: http://jsfiddle.net/k4cp4/4/
Where this can easily be put into a function, like:
var checkMatch = (function () {
var availableTags = [
{ label: "Yoga classes", category: "EDUCATIONAL" },
{ label: "Cooking classes", category: "EDUCATIONAL" },
{ label: "Cheese tastings", category: "EDUCATIONAL" },
{ label: "Maker Workshops", category: "PRACTICAL" },
{ label: "Seminars", category: "PRACTICAL" }
];
return function (input) {
var found = false;
for (var i = 0, j = availableTags.length; i < j; i++) {
var cur = availableTags[i];
if (cur.label === input) {
found = true;
break;
}
}
return found;
};
})();
DEMO: http://jsfiddle.net/k4cp4/5/
This checks for an exact match. So if you want a case insensitive match, you can use:
if (cur.label.toLowerCase() === input.toLowerCase()) {
DEMO: http://jsfiddle.net/k4cp4/6/
If you want to see if any of the labels contain the input, you can use indexOf like:
if (cur.label.indexOf(input) > -1) {
DEMO: http://jsfiddle.net/k4cp4/7/
You can use Array.some method:
Tests whether some element in the array passes the test implemented by the provided function.
Then your code would look something like:
var isFound = availableTags.some(function(el) {
return el.label === 'Yoga classes';
});
Note: some method needs to be shimmed.
var check = function(item) {
for(at in availableTags) {
if(item == availableTags[at].label) {
return true;
}
}
return false;
}
console.log(check("Yoga classes"));

Categories