I'm trying to return the Label match however I seem to be doing something wrong here. Can someone push me in the right direction?
console.log('start');
var test = {
"ID": 234324,
"Label": "KDF",
"children": [{
"ID": 234234,
"Label": "KJDF",
"children": [{
"ID": 234324,
"Label": "KJDF"
}, {
"ID": 22323,
"Label": "LKNDF"
}, {
"ID": 34535,
"Label": "LKNSF"
}]
}, {
"ID": 323434,
"Label": "CLK"
}]
}
function testThis(thing, ID) {
if (thing.ID == ID) {
console.log('match!')
return thing.Label;
} else if (thing.children && thing.children.length) {
thing.children.forEach(function(x) {
console.log(x);
return testThis(x, ID);
})
return false;
} else {
return false;
}
}
console.log(testThis(test, 323434));
console.log('end');
Where you do
thing.children.forEach(function(x) {
use .some() instead of .forEach(), like this
return thing.children.some(function(x) {})
.forEach() returns undefined, while .some() will return either true or false and will stop iterating once trueis returned.
some() executes the callback function once for each element present in the array until it finds one where callback returns a true value. If such an element is found, some() immediately returns true. Otherwise, some() returns false.
Fiddle: https://jsfiddle.net/mkarajohn/7ebr6pna/
You don't need to use foreach for that, use a normal for like this
function testThis(thing, ID) {
if (thing.ID == ID) {
return thing.Label;
} else if (thing.children && thing.children.length) {
var label;
var length = thing.children.length
for(var i = 0; i < length; i++) {
label = testThis(thing.children[i], ID);
if(label) {
return label;
}
}
}
return false;
}
You're expecting forEach to return something and leave the loop. It always returns undefined and always iterates every element.
forEach() executes the callback function once for each array element;
unlike map() or reduce() it always returns the value undefined and is
not chainable. The typical use case is to execute side effects at the
end of a chain.
See MDN forEach
The Array.prototype.some() might serve you better since it will only execute some of the elements and returns a value.
See working code below:
console.log('start');
var test = {
"ID": 1,
"Label": "A",
"children": [{
"ID": 2,
"Label": "B",
"children": [{
"ID": 5,
"Label": "E"
}, {
"ID": 6,
"Label": "F"
}, {
"ID": 7,
"Label": "G"
}]
}, {
"ID": 3,
"Label": "C"
}, {
"ID": 4,
"Label": "D",
"children": [{
"ID": 8,
"Label": "H"
}, {
"ID": 9,
"Label": "I"
}]
}]
}
function testThis(thing, ID) {
if (thing.ID == ID) {
console.log('match!')
return thing.Label;
} else if (thing.children && thing.children.length) {
var theone = null;
thing.children.some(function(x) {
theone = testThis(x, ID);
return theone;
})
return theone;
} else {
return false;
}
}
alert(testThis(test, 5));
console.log('end');
Related
this.StaticData = {
"values": [
{
"value": "test",
"label": "test"
},
{
"value": "aa",
"label": "bb"
},
{
"value": "cc",
"label": "dd"
}
]
};
I have above object of data. I wanted to return all object except currentIndex.
For example -
suppose in above objects, if I am going to edit 0th index values,
and I have updated "value": "rest", instead of "value": "test" and
"label": "test" need to keep as it is. So in that case,
it will allow to update the values.
{
"value": "rest",
"label": "test"
},
But if I tried to enter "label": "bb" and "label": "dd",
so it will return false, because these values are already available in above objects.
isLabelExist() {
const formData = this.editStaticParametersForm.value;
const currentIndex: number = this.StaticData.values.indexOf(this.selectedRowValue);
if (formData.label_value && this.StaticData) {
var isPresent = this.StaticData.values.some(function (el) {
return el.label === formData.label_value
});
if (isPresent) {
return false;
}
}
return true;
}
using find (or some) you can check the "index" (add a second argument to the function find), so,
var isPresent = this.StaticData.values.some(function (el,i) {
return el.label === formData.label_value && i!=currentIndex
});
Really in .ts we use arrow flat and use const or let, not var
const isPresent = this.StaticData.values.some((el,i)=> {
return el.label === formData.label_value && i!=currentIndex
});
Or
const isPresent = this.StaticData.values.some(
(el,i)=> el.label === formData.label_value && i!=currentIndex);
I'm trying to figure out how to search recursively element in json tree. For example I'm trying right now to reach 'Fruits' but can't figure out how to do it. Here's my json object
[
{
"_id": "604cab0acbdb8c1060698419",
"name": "Grocery",
"children": [
{
"children": [
{
"name": "Fruits",
"price": "200"
}
],
"_id": "604cad9b4ae51310c6f313f6",
"name": "Potatoes",
"price": "200"
},
{
"children": [],
"_id": "604cae721257d510e679a467",
"name": "Vegetables"
}
],
"date": "2021-03-13T12:07:38.186Z",
"__v": 0
} ]
function findName(name, tree) {
if(tree.children.name == name {
return tree;
}
if(tree.child == 0) {
return
}
return findName(name, tree);
};
There are a couple of issues with your implementation.
Your starting point is an array, but you treat it as though it were a node by trying to use its children property.
You're looking for name on children, but children is an array.
You're passing the same thing into findName at the end that you received. If you reach that point, you'll constantly call yourself until you run out of stack space.
Loop through the nodes in the array checking them and their children; see comments:
function findName(name, children) {
if (Array.isArray(children)) {
// Yes, check them
for (const childNode of children) {
if (childNode.name === name) {
// Found it
return childNode;
}
// Look in this node's children
const found = findName(name, childNode.children);
if (found) {
// Found in this node's children
return found;
}
}
}
}
Live Example:
const tree = [
{
"_id": "604cab0acbdb8c1060698419",
"name": "Grocery",
"children": [
{
"children": [
{
"name": "Fruits",
"price": "200"
}
],
"_id": "604cad9b4ae51310c6f313f6",
"name": "Potatoes",
"price": "200"
},
{
"children": [],
"_id": "604cae721257d510e679a467",
"name": "Vegetables"
}
],
"date": "2021-03-13T12:07:38.186Z",
"__v": 0
} ];
function findName(name, children) {
if (Array.isArray(children)) {
// Yes, check them
for (const childNode of children) {
if (childNode.name === name) {
// Found it
return childNode;
}
// Look in this node's children
const found = findName(name, childNode.children);
if (found) {
// Found in this node's children
return found;
}
}
}
}
console.log(findName("Fruits", tree));
const object = [
{
"_id": "604cab0acbdb8c1060698419",
"name": "Grocery",
"children": [
{
"children": [
{
"name": "Fruits",
"price": "200"
}
],
"_id": "604cad9b4ae51310c6f313f6",
"name": "Potatoes",
"price": "200"
},
{
"children": [],
"_id": "604cae721257d510e679a467",
"name": "Vegetables"
}
],
"date": "2021-03-13T12:07:38.186Z",
"__v": 0
} ]
function find(name, tree) {
// tree is undefined, return `undefined` (base case)
if (!tree) return
if (Array.isArray(tree)) {
// tree is an array
// so iterate over every object in the array
for (let i = 0; i < tree.length; i++) {
const obj = tree[i]
const result = find(name, obj)
// `find` returned non-undefined value
// means match is found, return it
if (result) return result
// no match found in `obj` so continue
}
} else if (tree.name === name) {
// `tree` is a key-value object
// and has matching `name`
return tree
}
// if no tree matching `name` found on the current `tree`
// try finding on its `children`
return find(name, tree.children)
}
console.log(find("Fruits", object))
I have an array with objects, like the following.
b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
I want to count how many issues have status close, and how many have backlog. I'd like to save the count in a new array as follows.
a = [
{Name: 'Backlog', count: 1},
{Name: 'close', count: 2}
];
I have tried the following.
b.issues.forEach(function(i) {
var statusName = i.fields.status.name;
if (statusName in a.Name) {
a.count = +1;
} else {
a.push({
Name: statusName,
count: 1
});
}
});
That however doesn't seem to be working. How should I implement this?
This is a perfect opportunity to use Array#reduce. That function will take a function that is applied to all elements of the array in order and can be used to accumulate a value. We can use it to accumulate an object with the various counts in it.
To make things easy, we track the counts in an object as simply {name: count, otherName: otherCount}. For every element, we check if we already have an entry for name. If not, create one with count 0. Otherwise, increment the count. After the reduce, we can map the array of keys, stored as keys of the object, to be in the format described in the question. See below.
var b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
var counts = b.issues.reduce((p, c) => {
var name = c.fields.status.name;
if (!p.hasOwnProperty(name)) {
p[name] = 0;
}
p[name]++;
return p;
}, {});
console.log(counts);
var countsExtended = Object.keys(counts).map(k => {
return {name: k, count: counts[k]}; });
console.log(countsExtended);
.as-console-wrapper {
max-height: 100% !important;
}
Notes.
Array#reduce does not modify the original array.
You can easily modify the function passed to reduce to for example not distinguish between Backlog and backlog by changing
var name = c.fields.status.name;
into
var name = c.fields.status.name.toLowerCase();
for example. More advanced functionality can also easily be implemented.
Using ES6 Arrow functions you can do it with minimum syntax
var b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
var countOfBackLog = b.issues.filter(x => {
return x.fields.status.name === "Backlog"
}).length
var countOfClose = b.issues.filter(x => {
return x.fields.status.name === "close"
}).length
a =[{Name: 'Backlog', count : countOfBackLog}, {Name: 'close', count : countOfClose}]
More about arrow functions here
You can write like this. It is dynamic.
var a = {};
for(var key in b["issues"]){
if(!a.hasOwnProperty(b["issues"][key].fields.status.name)){
a[b["issues"][key].fields.status.name] = 1;
}else{
a[b["issues"][key].fields.status.name] = a[b["issues"][key].fields.status.name]+1;
}
}
var c = [];
for(var key1 in a){
c.push({
name : key1,
count : a[key1]
});
}
Something like this should do the trick. Simply iterate over your data, keep 2 counters with the number of each type of issue, and create the data format you want in the end. Try it live on jsfiddle.
var b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
var data = [];
for(var issue of b.issues){
var entryFound = false;
var tempObj = {
name: issue.fields.status.name,
count: 1
};
for(var item of data){
if(item.name === tempObj.name){
item.count++;
entryFound = true;
break;
}
}
if(!entryFound){
data.push(tempObj);
}
}
console.log(data);
I have a question regarding displaying a path from a tree depending on the value of the leaf, for example I have the following JSON :
{
"children":
[
{
"children":
[
{
"name": "Predict Conversion"
}
],
"name": "Browser ID in {1}"
},
{
"children":
[
{
"name": "Predict Click"
}
],
"name": "Browser ID not in {1}"
}
],
"name": "Device Type ID in {1,3,4}"
}
I want to display only the full path leading to the leaf with value = "Predict Conversion"
You can use recursion to loop over object. Use Array.isArray to test if value is of type Array and typeof(obj)==="object" for object.
Note: typeof(obj) will return object for both Array and for Object
function searchInObj(obj, value, result) {
// check for array and call for every item
if (Array.isArray(obj)) {
// primary flag for array.
var r = false;
obj.forEach(function(item, index) {
// temporary flag for every iteration.
var _r = searchInObj(item, value, result);
if (_r) result.push(index)
// if one of element returned true, array should return true.
r = _r || r;
});
return r;
}
// If Object, loop over properties
else if (typeof(obj) === "object") {
for (var k in obj) {
// If object, check if property is Object/Array and call self.
if (typeof(obj[k]) === "object") {
var r = searchInObj(obj[k], value, result);
if (r) result.push(k);
return r;
}
// If property is not Array/Object, match value
else if (obj[k] === value) {
result.push(k);
return true;
}
// If no match, return false
else {
return false;
}
}
}
}
var data = {
"children": [{
"children": [{
"name": "Predict Conversion"
}],
"name": "Browser ID in {1}"
}, {
"children": [{
"name": "Predict Click"
}],
"name": "Browser ID not in {1}"
}],
"name": "Device Type ID in {1,3,4}"
}
var result = []
searchInObj(data, "Predict Conversion", result);
document.write("<pre>" + JSON.stringify(result.reverse(), 0, 4) + "</pre>");
Note: For small JSON, this will work but if your JSON is very long, this can be very expensive operation.
You can use recursion for it.
Quick example
var tree = {
"children": [{
"children": [{
"name": "Predict Conversion"
}],
"name": "Browser ID in {1}"
}, {
"children": [{
"name": "Predict Click"
}],
"name": "Browser ID not in {1}"
}],
"name": "Device Type ID in {1,3,4}"
};
console.debug(tree);
function getPath(node, value){
if(typeof node.children !== "undefined" && node.children !== null){
for(var index in node.children){
var name = getPath(node.children[index], value);
if(name) {
return node.name+"."+name;
}
}
} else {
if(node.name === value){
return node.name;
}
return false;
}
}
console.log(getPath(tree, "Predict Conversion"))
Working example in this fiddle
I have a JSON list with duplicates I need to remove, but I can't find a way to do it.
This is the solution that I have.
I want to keep the first item found with a given ID, and remove the next ones with the same ID.
The problem is, it tries to remove even the first item.
var gindex = [];
function removeDuplicate(list) {
$.each(list, function(i, val){
console.log(val.id);
console.log(gindex);
if($.inArray(val.id, gindex) == -1) { //in array, so leave this item
gindex.push(val.id);
}
else // found already one with the id, delete it
{
list.splice(i, 1);
}
if(val.children) {
val.children = removeDuplicate(val.children);
}
});
return list;
}
gindex = [];
list = removeDuplicate(parsed_list);
console.log(window.JSON.stringify(list));
finally, this is the original list :
[
{
"id": 0,
"children": [
{
"id": 1,
"children": [
{
"id": 2, // with my algorithm, this one get also flagged for deletion
}
]
},
{
"id": 2, // remove this one
},
{
"id": 3,
},
{
"id": 4, // with my algorithm, this one get also flagged for deletion
"children": [
{
"id": 5, // with my algorithm, this one get also flagged for deletion
"children": [
{
"id": 6, // with my algorithm, this one get also flagged for deletion
}
]
}
]
},
{
"id": 5, // remove this one
"children": [
{
"id": 6, // remove this one
}
]
},
{
"id": 6, // remove this one
},
{
"id": 7,
}
]
}
]
and this is the result I would like to obtain
[
{
"id": 0,
"children": [
{
"id": 1,
"children": [
{
"id": 2,
}
]
},
{
"id": 3,
},
{
"id": 4,
"children": [
{
"id": 5,
"children": [
{
"id": 6,
}
]
}
]
},
{
"id": 7,
}
]
}
]
thank you for your reply.
I tried creating my own logic for this (probably more general than what you want), but it may help you debug your code. See the jsFiddle.
The core of the logic is
/**
* Walk through an object or array and remove duplicate elements where the 'id' key is duplicated
* Depends on a seenIds object (using it as a set)
*/
function processData(el) {
// If the element is an array...
if ($.isArray(el)) {
for (var i = 0; i < el.length; i++) {
var value = el[i];
processData(value);
// If the child is now empty, remove it from the array
if (checkForEmpty(value)) {
el.splice(i, 1);
i--; // Fix index after splicing (http://stackoverflow.com/a/9882349/1370556)
}
}
}
// If the element is an object...
else if ($.isPlainObject(el)) {
for (var key in el) {
// Make sure the key is not part of the prototype chain
if (el.hasOwnProperty(key)) {
var value = el[key];
if (key == 'id') {
// If the key has been seen, remove it
if (seenIds[value]) {
delete el[key];
continue; // Skip further processing
} else seenIds[value] = true;
}
processData(value);
// If the child is now empty, remove it from the object
if (checkForEmpty(value)) delete el[key];
}
}
}
}