Javascript, remove property from one of two similar objects in array - javascript

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);

Related

JavaScript buidling a hierarchical tree and make out put nested UL/LI inside expressjs

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/

Rename json keys iterative

I got a very simple json but in each block I got something like this.
var json = {
"name": "blabla"
"Children": [{
"name": "something"
"Children": [{ ..... }]
}
And so on. I don't know how many children there are inside each children recursively.
var keys = Object.keys(json);
for (var j = 0; j < keys.length; j++) {
var key = keys[j];
var value = json[key];
delete json[key];
key = key.replace("Children", "children");
json[key] = value;
}
And now I want to replace all "Children" keys with lowercase "children". The following code only works for the first depth. How can I do this recursively?
It looks the input structure is pretty well-defined, so you could simply create a recursive function like this:
function transform(node) {
return {
name: node.name,
children: node.Children.map(transform)
};
}
var json = {
"name": "a",
"Children": [{
"name": "b",
"Children": [{
"name": "c",
"Children": []
}, {
"name": "d",
"Children": []
}]
}, {
"name": "e",
"Children": []
}]
};
console.log(transform(json));
A possible solution:
var s = JSON.stringify(json);
var t = s.replace(/"Children"/g, '"children"');
var newJson = JSON.parse(t);
Pros: This solution is very simple, being just three lines.
Cons: There is a potential unwanted side-effect, consider:
var json = {
"name": "blabla",
"Children": [{
"name": "something",
"Children": [{ ..... }]
}],
"favouriteWords": ["Children","Pets","Cakes"]
}
The solution replaces all instances of "Children", so the entry in the favouriteWords array would also be replaced, despite not being a property name. If there is no chance of the word appearing anywhere else other than as the property name, then this is not an issue, but worth raising just in case.
Here is a function that can do it recursivly:
function convertKey(obj) {
for (objKey in obj)
{
if (Array.isArray(obj[objKey])) {
convertKey[objKey].forEach(x => {
convertKey(x);
});
}
if (objKey === "Children") {
obj.children = obj.Children;
delete obj.Children;
}
}
}
And here is a more generic way for doing this:
function convertKey(obj, oldKey, newKey) {
for (objKey in obj)
{
if (Array.isArray(obj[objKey])) {
obj[objKey].forEach(objInArr => {
convertKey(objInArr);
});
}
if (objKey === oldKey) {
obj[newKey] = obj[oldKey];
delete obj[oldKey];
}
}
}
convertKey(json, "Children", "children");
Both the accepted answer, and #Tamas answer have slight issues.
With #Bardy's answer like he points out, there is the issue if any of your values's had the word Children it would cause problems.
With #Tamas, one issue is that any other properties apart from name & children get dropped. Also it assumes a Children property. And what if the children property is already children and not Children.
Using a slightly modified version of #Tamas, this should avoid the pitfalls.
function transform(node) {
if (node.Children) node.children = node.Children;
if (node.children) node.children = node.children.map(transform);
delete node.Children;
return node;
}
var json = {
"name": "a",
"Children": [{
"age": 13,
"name": "b",
"Children": [{
"name": "Mr Bob Chilren",
"Children": []
}, {
"name": "d",
"age": 33, //other props keep
"children": [{
"name": "already lowecased",
"age": 44,
"Children": [{
"name": "now back to upercased",
"age": 99
}]
}] //what if were alrady lowercased?
}]
}, {
"name": "e",
//"Children": [] //what if we have no children
}]
};
console.log(transform(json));

Evaluating key values in multi-dimensional object

I have a multi-dimensional object that looks like this:
{
"links": [{"source":"58","target":"john","total":"95"},
{"source":"60","target":"mark","total":"80"}],
"nodes":
[{"name":"john"}, {"name":"mark"}, {"name":"rose"}]
}
I am trying to evaluate the value of "total" in "links." I can do this in a one-dimensional array, like this:
for (var i = 0; i < data.length; i++) {
for (var key in data[i]) {
if (!isNaN(data[i][key])) {
data[i][key] = +data[i][key];
}
}
};
But I have not been able to figure out how to do this in two-dimensions (especially calling the value of key "total" by name).
Can anybody set me on the right track? Thank you!
Starting from the principle that the structure of your array is this, you can to iterate the keys and the values:
var obj = {
"links": [{"source":"58","target":"john","total":"95"},
{"source":"60","target":"mark","total":"80"}],
"nodes":
[{"name":"john"}, {"name":"mark"}, {"name":"rose"}]
};
for (var key in obj){
obj[key].forEach(function(item){
for(var subkey in item){
if (subkey == 'total')
console.log(item[subkey]);
};
});
};
You can get total using reduce
check this snippet
var obj = {
"links": [{
"source": "58",
"target": "john",
"total": "95"
}, {
"source": "60",
"target": "mark",
"total": "80"
}, {
"source": "60",
"target": "mark",
"total": "80"
}],
"nodes": [{
"name": "john"
}, {
"name": "mark"
}, {
"name": "rose"
}]
}
var links = obj.links;
var sum = links.map(el => el.total).reduce(function(prev, curr) {
return parseInt(prev, 10) + parseInt(curr, 10);
});
console.log(sum);
Hope it helps
Extract the values from the array, convert them to numbers and add them up.
Array.prototype.map() and Array.prototype.reduce() are pretty helpful here:
var data = {"links":[{"source":"58","target":"john","total":"95"},{"source":"60","target":"mark","total":"80"}], "nodes":[{"name":"john"}, {"name":"mark"}, {"name":"rose"}]};
var sum = data.links.map(function(link) {
return +link.total;
}).reduce(function(a, b) {
return a + b;
});
console.log(sum);

change the value of object using loop [duplicate]

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)
}
})
`

Iterate over a list of values using javascript

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 ;)

Categories

Resources