I am looking to iterate over a list of values using javascript.
I have a list like this
Label: A Value: Test Count: 4
Label: B Value: Test2 Count: 2
Label: C Value: Test3 Count: 4
Label: D Value: Test4 Count: 1
Label: C Value: Test5 Count: 1
My goal is to pass each row into different functions based on the label. I am trying to figure out if a multidimensional array is the best way to go.
var list = [
{"Label": "A", "value": "Test", "Count": 4},
{"Label": "B", "value": "Test2", "Count": 2},
{"Label": "C", "value": "Test3", "Count": 4},
{"Label": "D", "value": "Test4", "Count": 1},
{"Label": "C", "value": "Test5", "Count": 1}
]
for(var i = 0, size = list.length; i < size ; i++){
var item = list[i];
if(matchesLabel(item)){
someFunction(item);
}
}
You get to define the matchesLabel function, it should return true if the item needs to be passed to your function.
well it's been 8 years but today you can use for ... of
const array1 = ['a', 'b', 'c'];
for (const element of array1) {
console.log(element);
}
source : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
If you would like to make it more pro, you can use this function
function exec(functionName, context, args )
{
var namespaces = functionName.split(".");
var func = namespaces.pop();
for(var i = 0; i < namespaces.length; i++) {
context = context[namespaces[i]];
}
return context[func].apply(this, args);
}
This function allows you to run it in context you want (typical scenario is window context) and pass some arguments. Hope this helps ;)
Related
I have below code from another request it's working fine, it's creating a hierarchical tree (deep level). But I need the output instead of JSON to be HTML UL/LI nested or select menu parent and child
The idea here is: need to do category and subcategory like WordPress. I will use it inside nodejs expressjs
Here is the data
var items = [
{"Id": "1", "Name": "abc", "Parent": "2"},
{"Id": "2", "Name": "abc", "Parent": ""},
{"Id": "3", "Name": "abc", "Parent": "5"},
{"Id": "4", "Name": "abc", "Parent": "2"},
{"Id": "5", "Name": "abc", "Parent": ""},
{"Id": "6", "Name": "abc", "Parent": "2"},
{"Id": "7", "Name": "abc", "Parent": "6"},
{"Id": "8", "Name": "abc", "Parent": "6"},
{"Id": "9", "Name": "abz", "Parent": "8"}];
A function that builds tree
function buildHierarchy(arry) {
var roots = [], children = {};
// find the top level nodes and hash the children based on parent
for (var i = 0, len = arry.length; i < len; ++i) {
var item = arry[i],
p = item.Parent,
target = !p ? roots : (children[p] || (children[p] = []));
target.push({ value: item });
}
// function to recursively build the tree
var findChildren = function(parent) {
if (children[parent.value.Id]) {
parent.children = children[parent.value.Id];
for (var i = 0, len = parent.children.length; i < len; ++i) {
findChildren(parent.children[i]);
}
}
};
// enumerate through to handle the case where there are multiple roots
for (var i = 0, len = roots.length; i < len; ++i) {
findChildren(roots[i]);
}
return roots;}
console.log(buildHierarchy(items));
You can do that with this simple recursive function
function treeToHtml(tree) {
var listItems = tree.map(function(node){
var result = `<li>${node.value.Name}</li>`;
if(node.children)
result += `<li>${treeToHtml(node.children)}</li>`;
return result;
}).join('')
return `<ul>${listItems}</ul>`
}
Here is the solution with a select:
function treeToSelect(tree, level) {
if(!level) level = 0;
return tree.map(function(node){
var indentation = "-".repeat(level * 3)
var result = `<option>${indentation}${node.value.Name}</option>`;
if(node.children)
result += treeToSelect(node.children, level + 1);
return result;
}).join('')
}
For the example above, just the options will be generated, so you just have to put it inside a select tag.
You can adjust the indentation with the character that you want and choose how many times it will appear each level by adjusting the level multiplier.
var indentation = "-".repeat(level * 3);
Check the live example: https://jsfiddle.net/tercio_garcia/fd08pbo3/4/
This input (tree-like structure) has to be formatted to a particular format to draw a d3 sankey diagram chart.
let unformattedJson = [
{
"key": "a1",
"value": 30,
"buckets": [
{
"key": "a2",
"value": 10
},
{
"key": "b2",
"value": 20
}
]
},
{
"key": "b1",
"value": 70,
"buckets": [
{
"key": "b2",
"value": 40
},
{
"key": "c2",
"value": 30
}
]
}
]
Expected output I need to generate is:
{
"nodes": [
{"nodeId":0,"name":"a1"},
{"nodeId":1,"name":"a2"},
{"nodeId":2,"name":"b2"},
{"nodeId":3,"name":"b1"},
{"nodeId":4,"name":"c2"}
],
"links": [
{"source":0,"target":1,"value":10},
{"source":0,"target":2,"value":20},
{"source":3,"target":2,"value":40},
{"source":3,"target":4,"value":30}
]
}
My approach for the solution. I made two functions to calculate node and links. For nodes, I made a recursive functions to get all the unique keys and assigned a id for each keys. And I made another functions to get all the relationship between the keys.
let makeNodeObj = function(orObj, index){
let obj = {};
obj.nodeId = index;
obj.name = orObj;
return obj;
}
var getUniqueKeys = (old, arr)=>{
let toRet = old;
arr.forEach((data,index)=>{
if(toRet.indexOf(data.key)<0){ //remove duplicates
toRet.push(data.key);
}
if(data.buckets !== undefined && data.buckets.length>0){
getUniqueKeys(toRet, data.buckets);
}
});
return toRet;
}
let uniqueKeys = getUniqueKeys([],unformattedJson);
let nodes = uniqueKeys.map((data,index)=>{
return makeNodeObj(data,index);
});
let getNodeId = function(nodes, key){
let node = nodes.find((data)=>{
return data.name == key
});
return node.nodeId;
}
let links = [];
unformattedJson.map((data)=>{
let sourceId = getNodeId(nodes, data.key);
if(data.buckets.length>0){
data.buckets.map((data2)=>{
let targetId = getNodeId(nodes,data2.key);
let linkObj = {};
linkObj.source = sourceId;
linkObj.target = targetId;
linkObj.value = data2.value;
links.push(linkObj);
})
}
});
console.log({
nodes, links
});
My solution will only work if there are only one level-deep buckets. How to achieve this for multiple nested buckets inside child?
I made a recursive approach for generate the expected output. Associations between a key and his generated id are keep with a Map. The approach uses the idea of traversing the tree with Deep First Search algorithm.
let unformattedJson = [
{
"key": "a1",
"value": 30,
"buckets": [
{
"key": "a2",
"value": 10,
"buckets": [
{"key": "a3", "value": 99}
]
},
{"key": "b2", "value": 20}
]
},
{
"key": "b1",
"value": 70,
"buckets": [
{"key": "b2", "value": 40},
{"key": "c2", "value": 30}
]
}
];
const getData = (input, visited=new Map(), parent, nodes=[], links=[]) =>
{
input.forEach(x =>
{
// Add node into the node list, if not visited previosuly.
if (!visited.has(x.key))
{
let currId = nodes.length;
nodes.push({nodeId: currId, name: x.key});
visited.set(x.key, currId);
}
// If a parent node exists, add relation into the links list.
if (parent)
{
// Note, we use the "Map" to get the ids.
links.push({
source: visited.get(parent.key),
target: visited.get(x.key),
value: x.value
});
}
// Traverse (if required) to the next level of deep.
if (x.buckets)
getData(x.buckets, visited, x, nodes, links)
});
return {nodes: nodes, links: links};
}
console.log(getData(unformattedJson));
Let's assume we have this data set:
var array = [
{
"name": "a",
"group": "a"
},
{
"name": "a",
"group": "a"
},{
"name": "b",
"group": "b"
},
{
"name": "b",
"group": "b"
},
{
"name": "c"
}
];
and I want to loop through the array to see if there are two objects have the same group value, then remove the second of them.
for(var i = 0 ; i<array.length;i++){
var a = array[i];
for(var j = 0; j< array.length;j++){
if(array[j].group == a.group){
var b = array[j];
// I need code here to remove property "group" from the variable b only
break;
}
}
}
the final results I want are:
var array2 = [
{
"name": "a",
"group": "a"
},
{
"name": "a"
},{
"name": "b",
"group": "b"
},
{
"name": "b"
},{
"name":"c"
}
];
NOTE: I tried delete array[j].group but it caused to remove both group property from both equal objects. How can I solve that?
You shouldn't compare same items, just shift indexes in inner loop:
var array = [{"name": "a", "group": "a"},
{"name": "a", "group": "a"},
{"name": "b", "group": "b"},
{"name": "b", "group": "b"},
{"name": "c"}];
for(var i = 0 ; i < array.length - 1; i++){
var a = array[i];
if(!a.group){
continue;
}
for(var j = i+1; j < array.length; j++){
var b = array[j];
if(b.group === a.group){
delete b.group;
}
}
}
console.log(array)
You can try this:
var tmpObj = {};
tmpObj.name = array[j].name;
array.splice(j, 1, tmpObj);
It should remove the element with index j and add new object with only name.
Just store all the group values you already have seen, and remove them if you see them again. Moreover, this will save you a loop.
var myArray = [...];
var existingGroups = [];
myArray.forEach(function(item){
if(item.group){
if(existingGroups.indexOf(item.group) === -1)
existingGroups.push(item.group);
else
delete item.group;
}
});
I'd go with a different approach:
Little explanation of the if condition:
array.slice(0, i): we take only the previous elements of the array.
.filter(v => v.group === val.group) we see if they have the same value for property group.
.length === 0) If there is at least one element with the same value of group, we do not enter the if and return only the name, otherwise we return the value itself
var array = [{"name": "a", "group": "a"},
{"name": "a", "group": "a"},
{"name": "b", "group": "b"},
{"name": "b", "group": "b"},
{"name": "c"}];
array = array.map((val, i) => {
if (array.slice(0, i).filter(v => v.group === val.group).length === 0) {
return val;
}
return {name: val.name};
})
console.log(array)
Here is a simple code which might help:
var groups = {};
array.forEach(function(o) {
if (groups[o.group]) {
delete o.group;
} else {
groups[o.group] = true;
}
})
You can also use more functional approach but you will need an additional utility library or have to implement some of the methods yourself.
var groups = array.map(function(o) { return o.group; }).unique();
groups
.map(function(group) {
return array.filter(function(o) { o.group == group }).slice(1);
})
.flatten()
.forEach(function(o) { delete o.group });
flatten & unique are not included in the JavaScript spec.
You don't need imbricated loops to do this. You can use .forEach() while keeping track of the groups that have been encountered so far. This can be done by using either the optional thisArg parameter or an explicit variable.
For instance:
var array = [
{ "name": "a", "group": "a" },
{ "name": "a", "group": "a" },
{ "name": "b", "group": "b" },
{ "name": "b", "group": "b" },
{ "name": "c" }
];
var grp = {};
array.forEach(function(o) {
grp[o.group] ? delete o.group : grp[o.group] = true;
});
console.log(array);
This question already has answers here:
JavaScript Object Mirroring/One-way Property Syncing
(2 answers)
Closed 7 years ago.
I have an object as,
var obj = [
{
"name": "a",
"value": "1"
},
{
"name": "b",
"value": "2"
},
{
"name": "c",
"value": "3"
}
]
I have a large object with more than 50 values.
how can I change the value key using its name
and what is the best looping technique for this.
I tried for loop for this like,
for(i = 0; i < obj.length; i++) {
if(obj[i].name == "b") {
// some other functionality
obj[i].value = "some_value";
}
}
But, it takes long time and sometimes for loop goes for next turn before if condition is executed.
Please explain how to solve it or is there any other looping technique
you can use forEach , but as far your hitting the performance its not best ,
you can use map but native for loop is fastest compared to map too
https://jsperf.com/native-map-versus-array-looping
Map , which runs on the each item of the array and return the new array
obj.map(function(item){
if(item.name === "b"){
item.value = "some_value"
}
return item;
})
You can try this :
$(document).ready(function(){
var obj = [
{
"name": "a",
"value": "1"
},
{
"name": "b",
"value": "2"
},
{
"name": "c",
"value": "3"
}
]
for(i = 0; i < obj.length; i++) {
(function(i){
if(obj[i].name === "b") {
console.log(obj[i].name);
// some other functionality
obj[i].value = "some_value";
}
})(i);
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I think what you had was quite ok. As one of the comments stated, there was a mistake in the IF-statement which prevented it from being triggered.
I am not sure theres a faster way to proces the JSON object than the way you did. Here's a JSFiddle with some small changes.
function ReplaceValue(name, val) {
for (i = 0; i < obj.length; i++) {
if (obj[i].name == name) {
// some other functionality
obj[i].value = val;
break;
}
}
alert(JSON.stringify(obj, null, 2));
}
Map is your friend!
var obj = [
{ "name": "a", "value": "1" },
{ "name": "b", "value": "2" },
{ "name": "c", "value": "3" }
];
var newObj = obj.map((elm) => {
if(elm.name === "b") elm.value = "some value";
return elm;
});
Is this something like what you were looking for?
In lodash you can do something like this:
`
var obj = [
{
"name": "a",
"value": "1"
},
{
"name": "b",
"value": "2"
},
{
"name": "c",
"value": "3"
}
];
_.transform(arr, function(r, n){
if(n.name == 'b'){
r.push({name: n.name, value: 'some value'})}
else{
r.push(n)
}
})
`
I have an array with the following structure:
var questions = [
{"answer":
["A1", "A2", "A3"]
},
{"answer":
["B1"]
},
{"answer":
["C1", "C2"]
}
]
I also have an array of objects like below:
var answers = [
{"value": "A1"},
{"value": "A512"},
{"value": ""},
{}
];
For each object in answers (if it's not {}) I would like to find if the corresponding value appears in some object inquestions. If it does, I would like to update a counter variable and remove that object from questions. How do I achieve this?
Try like this
var questions = [{
"answer": ["A1", "A2", "A3"]
}, {
"answer": ["B1"]
}, {
"answer": ["C1", "C2"]
}];
var count = 0;
answers.forEach(function(ans) {
var flag = isExists(ans.value);
flag && count++;
});
console.log(count);
function isExists(value) {
return questions.some(function(ques) {
var ind = ques.answer.indexOf(value);
if (ind > -1) {
ques.answer.splice(ind, 1);
return true;
}
});
}