I have the following structure:
var struct = [{
"name": "Name1",
"type": "type1",
"children": [{
"name": "Name11",
"type": "type1",
"children": [{
"name": "Name111",
"type": "type1"
},
{
"name": "Name112",
"type": "type2"
}
}]
}]
}];
I want to iterate over all name-type elements where type is equal to type1 and create an <option> element for each one of them.
So it would be something like:
var iterate = function (data) {
var elements = [];
for (var i in data) {
if (data[i].type == 'type1') {
var option = document.createElement('option');
option.innerHTML = data[i].name;
elements.push(option);
elements.push(iterate(data[i].children));
}
}
return elements;
};
It however gives me wrong results. And probably is very inefficient. How should I do this properly?
You could use an iterative recursive approach for getting the nested data.
function getElements(data) {
var elements = [];
data.forEach(function iter(a) {
var option;
if (a.type === 'type1') {
option = document.createElement('option');
option.innerHTML = a.name;
elements.push(option);
Array.isArray(a.children) && a.children.forEach(iter); // assuming only
} // type1 branches
}); // to iterate
return elements;
}
var struct = [{ "name": "Name1", "type": "type1", "children": [{ "name": "Name11", "type": "type1", "children": [{ "name": "Name111", "type": "type1" }, { "name": "Name112", "type": "type2" }] }] }];
console.log(getElements(struct));
I think that you must not use any "element" var, just try this:
var iterate = function (data) {
for (var i in data) {
if (data[i].type == 'type1') {
var option = document.createElement('option');
option.innerHTML = data[i].name;
data[i].push(option);
iterate(data[i].children);
}
}
return data;
};
Related
I create a Pokédex app, but i ran in some Problems with the double type Pokémon:
I call pokeapi twice to 2 endpoints (one for each Type), and i need to compare the Results in different Ways.
let a = {
"double_damage_from": [
{
"name": "ground",
"url": "https://pokeapi.co/api/v2/type/5/"
},
{
"name": "rock",
"url": "https://pokeapi.co/api/v2/type/6/"
},
{
"name": "water",
"url": "https://pokeapi.co/api/v2/type/11/"
}
],
"half_damage_from": [
{
"name": "bug",
"url": "https://pokeapi.co/api/v2/type/7/"
},
{
"name": "steel",
"url": "https://pokeapi.co/api/v2/type/9/"
},
{
"name": "fire",
"url": "https://pokeapi.co/api/v2/type/10/"
},
{
"name": "grass",
"url": "https://pokeapi.co/api/v2/type/12/"
},
{
"name": "ice",
"url": "https://pokeapi.co/api/v2/type/15/"
},
{
"name": "fairy",
"url": "https://pokeapi.co/api/v2/type/18/"
}
],
"no_damage_from": []
};
let b = {
"double_damage_from": [
{
"name": "fighting",
"url": "https://pokeapi.co/api/v2/type/2/"
},
{
"name": "ground",
"url": "https://pokeapi.co/api/v2/type/5/"
},
{
"name": "steel",
"url": "https://pokeapi.co/api/v2/type/9/"
},
{
"name": "water",
"url": "https://pokeapi.co/api/v2/type/11/"
},
{
"name": "grass",
"url": "https://pokeapi.co/api/v2/type/12/"
}
],
"half_damage_from": [
{
"name": "normal",
"url": "https://pokeapi.co/api/v2/type/1/"
},
{
"name": "flying",
"url": "https://pokeapi.co/api/v2/type/3/"
},
{
"name": "poison",
"url": "https://pokeapi.co/api/v2/type/4/"
},
{
"name": "fire",
"url": "https://pokeapi.co/api/v2/type/10/"
}
],
"no_damage_from": []
};
I need to Compare the Data and get the Matches in a array.
This works fine and i got the 4x, 1x, and 1/4x damage in a array:
getMatch(a, b) {
let matches = [];
for (let i = 0; i < a.length; i++) {
for (let e = 0; e < b.length; e++) {
if (a[i].name === b[e].name) matches.push(a[i]);
}
}
return matches;
}
compareTypes(a, b) {
let four_damage_from = this.getMatch(a.double_damage_from, b.double_damage_from);
let double_damage_from = [];
let normal_damage_from = this.getMatch(a.double_damage_from, b.half_damage_from);
let half_damage_from = [];
let quarter_damage_from = this.getMatch(a.half_damage_from, b.half_damage_from);
let no_damage_from = this.getMatch(a.no_damage_from, b.no_damage_from);
let matches = { four_damage_from, double_damage_from, normal_damage_from, half_damage_from, quarter_damage_from, no_damage_from };
return matches;
}
to find the correct types for double_damage_from i have to merge a.double_damage_from and b.double_damage_from e.g. to c.double_damage_from. then i have to remove from c.double_damage_from all types that are in four_damage_from, normal_damage_from, quarter_damage_from, no_damage_from to get the correct 2x types, the same with half_damage_from.
I tried many solution but i didn't figure out how to solve this.
greetings Raphi
It Works with lodash, i need to think a bit about but basically works.
removeMatch(union, remove, double = false) {
union = _.differenceBy(union, remove.four_damage_from, 'name');
union = _.differenceBy(union, remove.normal_damage_from, 'name');
union = _.differenceBy(union, remove.quarter_damage_from, 'name');
// union = _.differenceBy(union, remove.no_damage_from, 'name');
if(double) {
union = _.differenceBy(union, remove.half_damage_from, 'name');
} else {
union = _.differenceBy(union, remove.double_damage_from, 'name');
}
return union;
}
I have an array of objects with the following format
var arr = [
{
"productId": "123456",
"productName": "Test Product 1",
"description": [
"This is delicious",
"Suitable for vegetarian"
],
"attributes": {
"internalId": "091283"
"category": "Dairy"
},
"order": 1
}
];
And I am trying to map into something like below
[
[{
{
"name": "productId",
"value": "123456"
},
{
"name": "productName",
"value": "Test Product 1"
},
{
"name": "description",
"value": ["This is delicious", "Suitable for vegetarian"]
},
{
"name": "attributes",
"value": {
{
"name": "internalId",
"value": "091283"
},
{
"name": "category",
"value": "Dairy"
}
}
},
{
"name": "order",
"value": 1
}
}]
]
I tried mapping simple properties before going further and now stuck at getting only the last property of each object in the loop.
Suppose I don't know what are the format of incoming data and how can I normalize the JSON object to the format I want?
normalizeJson = (array) => {
for(i = 0; i < array.length; i++){
normalizedJson[i] = {};
Object.keys(array[i]).forEach(key => {
if (array[i][key] && typeof array[i][key] === "object") {
// normalizeJson(obj[key]);
// console.log(key + ' is object');
return;
} else {
o = {};
o["name"] = key;
o["value"] = array[i][key];
normalizedJson[i] = o;
// normalizedJson[i]["name"] = key;
// normalizedJson[i].value = array[i][key];
// console.log(key);
return;
}
});
}
console.log(normalizedJson);
};
Or is there any library I can use in order to achieve this?
Try this
var obj = [
{
productId: "123456",
productName: "Test Product 1",
description: ["This is delicious", "Suitable for vegetarian"],
attributes: {
internalId: "091283",
category: "Dairy",
},
order: 1,
},
];
function normalizeObject(obj) {
var result = [];
if (Array.isArray(obj)) {
for (let i of obj) {
result.push(normalizeObject(i));
}
} else if (typeof obj == "object") {
for (let i of Object.keys(obj)) {
result.push({ name: i, value: normalizeObject(obj[i]) });
}
} else {
return obj;
}
return result;
}
console.log(JSON.stringify(normalizeObject(obj), null, 2));
This looping method called recursion. Which is loop by calling function itself.
I need to transform below Json array to another array as mentioned below using angularjs/ javascript.
Input Array = [{"Name":"123", "Type": "Type1", "Total":"24"}, {"Name":"123", "Type": "Type2", "Total":"25"}, {"Name":"124", "Type": "Type1", "Total":"26"}, {"Name":"124", "Type": "Type2", "Total":"27"}]
Output Array: [{"Name":"123", "Type1":"24", "Type2":"25"}, {"Name":"124", "Type1":"26", "Type2":"27"}
I would work it out with reduce function I've added some comments for you inside as well:
let inputArray = [{"Name":"123", "Type": "Type1", "Total":"24"}, {"Name":"123", "Type": "Type2", "Total":"25"}, {"Name":"124", "Type": "Type1", "Total":"26"}, {"Name":"124", "Type": "Type2", "Total":"27"}];
// and do reduce function on it
inputArray.reduce((prevVal, currVal, index) => {
// first check if there's Name in already
if (prevVal.find(x => x.Name === currVal.Name)) {
// if there's a Name same as current element use new type as a key and add Total
prevVal.find(x => x.Name === currVal.Name)[currVal.Type] = currVal.Total;
} else {
// in case there's no Name then just create object with it
prevVal.push({
Name: currVal.Name,
[currVal.Type]: currVal.Total
});
}
return prevVal;
}, []);
Here's fiddle:
https://jsfiddle.net/pegla/s9hwbth4/
All you have to do is, get the unique objects from the original list based on Name.
var obj = [{
"Name": "123",
"Type": "Type1",
"Total": "24"
},
{
"Name": "123",
"Type": "Type2",
"Total": "25"
},
{
"Name": "124",
"Type": "Type1",
"Total": "26"
},
{
"Name": "124",
"Type": "Type2",
"Total": "27"
}
];
var getIndex = function(list, property, object) {
for (var i = 0; i < list.length; i++) {
if (list[i][property] == object[property]) {
return i;
}
}
return -1;
}
var result = [];
for (var i = 0; i < obj.length; i++) {
/* Call getIndex to return the index of element in the result and add the type name property */
var index = getIndex(result, 'Name', obj[i]);
if (index != -1) {
result[index][obj[i].Type] = obj[i].Total;
} else {
var newObj = {};
newObj.Name = obj[i].Name;
newObj[obj[i].Type] = obj[i].Total;
result.push(newObj);
}
}
console.log(result);
You don't need angularjs to do that. You can do that using simple javascript for block. Please see attached code. Best,
let input = [{"Name":"123", "Type": "Type1", "Total":"24"}, {"Name":"123", "Type": "Type2", "Total":"25"}, {"Name":"124", "Type": "Type1", "Total":"26"}, {"Name":"124", "Type": "Type2", "Total":"27"}];
let output = [];
for(let i=0; i<input.length-1;i++){
output.push({
"Name":input[i].Name,
"Type1":input[i].Total,
"Type2":input[i+1].Total,
});
i++;
}
console.log(output);
I'm struggling with the following issue:
I have a nested object. From the server I get a response with an object with changed values. So I want to find the object in my nested object and replace it.
My object has a structure like this:
$scope.page = {
id: 5,
label: 'myPage',
items : [
{
"type": "Container",
"id": 1,
"label": "header",
"items": [
{
"type": "Container",
"id": 2,
"label": "left",
"items": [
{
"type": "Menu",
"label": "settings-menu",
"id": "5"
},
{
"type": "Menu",
"label": "main-menu",
"id": "7"
}
]
},
{
"type": "Container",
"id": 4,
"label": "right",
"items": [
{
"type": "Post",
"label": "contact",
"id": "25"
}
]
}
]
},
{
"type": "Postlist",
"label": "nieuwsberichten",
"id": "17"
},
{
"type": "HTML",
"label": "over deze site",
"id": "18"
},
{
"type": "Other",
"label": "twitter feed",
"id": "19"
}
]
}
From the server I get a new object:
var newItem = {
"type": "Post",
"label": "contact",
"id": "25"
}
How can I update the object inside $scope.page the right way? I've tried the following:
$scope.findAndReplace(newItem,$scope.page.items);
$scope.findAndReplace = function(newItem, items) {
for (var i = 0; i < items.length; i++) {
if (items[i].id == newItem.id) {
items[i] = newItem;
} else if (items[i].items) {
$scope.findAndReplace(newItem, items[i].items);
}
}
}
and:
var oldItem = $scope.findById(item.id, $scope.page.items);
oldItem = newItem;
$scope.findById = function(id, items) {
var match = null;
angular.forEach(items, function(i){
if (match == null) {
if (i.id == id) {
match = i;
} else if (i.items) {
match = $scope.findById(id, i.items)
}
}
})
return match;
}
Neither of these options work. That's because of the nested loops where the object isn't the one in $scope.page anymore.
Anyone an idea to handle this?
Your example looks fine, can't understand why they are not working.
Neither of these options work. That's because of the nested loops where the object isn't the one in $scope.page anymore.
You can keep object reference by using angular.copy(newItem, oldItem)
Hi I have created a fiddle for you.
click for fiddle
for(var indx=0; indx < $scope.page.items.length; indx++) {
var tmpObj = $scope.page.items[indx];
if(tmpObj.hasOwnProperty('items')) {
// check inside
for(var indx1=0; indx1<tmpObj.items.length; indx1++ ) {
var innerObj = tmpObj.items[indx1];
// check for next level
if(innerObj.hasOwnProperty('items')) {
for(var counter=0; counter< innerObj.items.length; counter++) {
var thirdTmp = innerObj.items[counter];
console.log('3rd level inner object', thirdTmp);
if(thirdTmp.id === newItem.id) {
innerObj.items[counter] = newItem;
tmpObj.items[indx1] = innerObj;
$scope.page.items[indx] = tmpObj;
}
}
}
}
} else if(tmpObj.id === newItem.id) {
$scope.page.items[indx] = newItem;
}
};
I faced with common issue converting simple flat data to hierarchical. I have found multiple topics about that but still can't get how to convert flat data exactly to necessary me hierarchical format
this my json
[
{
"id": 1,
"name": "Sponsor",
"description": null,
"parentId": null
},
{
"id": 2,
"name": "Class",
"description": null,
"parentId": 1
},
{
"id": 3,
"name": "Study",
"description": null,
"parentId": 2
},
{
"id": 4,
"name": "Site",
"description": null,
"parentId": 3
}
]
and I need to get format like this
[
{
"data":{
"id": 1,
"name":"Sponsor",
"description":null,
"parentId":"null"
},
"children":[
{
"data":{
"id": 2,
"name":"Class",
"description":null,
"parentId":"1"
},
"children":[
{
"data":{
"id": 3,
"name":"Study",
"description":null,
"parentId":"2"
},
"children": [
{
"data":{
"id": 4,
"name":"Site",
"description":null,
"parentId":"3"
}
}
]
}
]
}
]
}
]
this is my function
flatToHierarchy(flat) {
let roots = [];
let all = {};
flat.forEach(function (item) {
all[item.id] = item
});
Object.keys(all).forEach(function (id) {
let item = all[id];
if (item.parentId === null) {
roots.push(item)
} else if (item.parentId in all) {
let p = all[item.parentId];
if (!('Children' in p)) {
p.children = []
}
p.children.push(item)
}
});
console.log(roots);
return roots
}
output
[
{
"id": 1,
"name": "Sponsor",
"description": null,
"parentId": null,
"children": [
{
"id": 2,
"name": "Class",
"description": "Together",
"parentId": 1,
"children": [
{
"id": 3,
"name": "Study",
"description": "browsing data",
"parentId": 2,
"children": [
{
"id": 4,
"name": "Site",
"description": null,
"parentId": 3,
"children": []
}
]
}
]
}
]
}
]
I'm pretty close to desire result. Could somebody to help me fix that ?
Edited
the right answer provided by #Someone3
this is slightly modified code for my needs
flatToHierarchy (flat) {
let roots = [];
let all = {};
let ids = [];
flat.forEach(function (item) {
let itemId = item.id;
let convertedItem = function (id) {
let newItem = {};
newItem['data'] = id;
return newItem;
} ;
all[itemId] = convertedItem(item);
ids.push(itemId);
});
for (let i = 0; i < ids.length; i++) {
let id = ids[i];
let convertedItem = all[id];
let parentId = convertedItem.data.parentId;
if (parentId === null) {
roots.push(convertedItem);
} else if (parentId in all) {
let p = all[parentId];
if (!('children' in p)) {
p.children = []
}
p.children.push(convertedItem)
}
}
return roots
}
The code below is full source code for your situation. I modified and added a few lines from your source code.
Note that this code assumes that parents are always inserted to this tree before their children do. If this assumption is not always true then your code need to be changed more than this.
let flatData = [
{
"id": 1,
"name": "Sponsor",
"description": null,
"parentId": null
},
{
"id": 2,
"name": "Class",
"description": null,
"parentId": 1
},
{
"id": 3,
"name": "Study",
"description": null,
"parentId": 2
},
{
"id": 4,
"name": "Site",
"description": null,
"parentId": 3
}
];
function convertItem(item) {
let newItem = {};
newItem.data = item;
return newItem;
}
function flatToHierarchy(flat) {
let roots = [];
let all = {};
let ids = [];
flat.forEach(function (item) {
let itemId = item.id;
let convertedItem = convertItem(item);
all[itemId] = convertedItem;
ids.push(itemId);
});
// We use ids array instead of object to maintain its previous order.
for (let i = 0; i < ids.length; i++) {
let id = ids[i];
let convertedItem = all[id];
let parentId = convertedItem.data.parentId;
if (parentId === null) {
delete convertedItem.data.parentId;
delete convertedItem.data.id;
roots.push(convertedItem);
} else if (parentId in all) {
let p = all[parentId];
if (!('Children' in p)) {
p.children = []
}
delete convertedItem.data.parentId;
delete convertedItem.data.id;
p.children.push(convertedItem)
}
};
console.log(roots);
return roots
}
flatToHierarchy(flatData);
We can factor out two deletes before push.
How about my way?
function flatToHierarchy (flatData) {
const tree = JSON.parse(JSON.stringify(flatData)) // or using `cloneDeep` of lodash library to not side-effect with flatData
tree.forEach((item) => {
item.children = tree.filter((element) => element.parent_id === dept.id)
});
const roots = tree.filter((item) => item.parent_id === 0)
return roots
}
const flatToHierarchy = (inputArr, parent = null) => {
const result = [];
for(let i = 0; i<inputArr.length; i++) {
if(inputArr[i].parentId === parent) {
const dataObj = {
data : {
...inputArr[i],
}
}
const children = flatToHierarchy(inputArr, inputArr[i].id);
if(children.length > 0) {
dataObj.children = children;
}
result.push(dataObj);
}
}
return result;
}