How to convert objects containing array into object of objects - javascript

My code:
function convert(arr, parent) {
var out = [];
for(var i in arr) {
if(arr[i].parent == parent) {
var children = convert(arr, arr[i].id)
if(children.length) {
arr[i].children = children
}
out.push(arr[i])
}
}
return out; //return Object.assign({}, out);tried this, but i lose parents childrens arrays
};
arras = [
{id: 1, name: "parent1", parent: null},
{id: 2, name: "children1", parent: 1},
{id: 3, name: "children2", parent: 1},
{id: 4, name: "parent2", parent: null},
{id: 5, name: "children3", parent: 4},
{id: 6, name: "children4", parent: 4}
]
console.log(convert(arras, null));
How final result should look
{
parent1: [
{name: "children1"},
{name: "children2"}
],
parent2: [
{name: "children3},
{name: "children4"}
]
}
What my output looks so far:
[
{id: 1, name: "parent1", parent: null}: [
{id: 2, name: "children1", parent: 1},
{id: 3, name: "children2", parent: 1},
],
{id: 4, name: "parent2", parent: null}: [
{id: 5, name: "children3", parent: 4},
{id: 6, name: "children4", parent: 4}
]
]
So firstly, what I have to do is convert main array to object, when I tend to do that, I lose both parent object arrays...Also need to change the way console displays objects, any help is appreciated.

You could build a tree with check if parent is a root node or not.
var data = [{ id: 1, name: "parent1", parent: null }, { id: 2, name: "children1", parent: 1 }, { id: 3, name: "children2", parent: 1 }, { id: 4, name: "parent2", parent: null }, { id: 5, name: "children3", parent: 4 }, { id: 6, name: "children4", parent: 4 }],
tree = function (data, root) {
var r = {},
o = {};
data.forEach(function (a) {
if (a.parent === root) {
r[a.name] = [];
o[a.id] = r[a.name];
} else {
o[a.parent] = o[a.parent] || [];
o[a.parent].push({ name: a.name });
}
});
return r;
}(data, null);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Try this.
function convert(arr) {
var parents = {};
for (var i in arr) {
if (arr[i].parent === null) {
parents[arr[i].id] = arr[i].name
}
}
var out = {}
for (i in arr) {
if (arr[i].parent !== null) {
var parentName = parents[arr[i].parent];
if (out.hasOwnProperty(parentName)) {
out[parentName].push(arr[i].name)
} else {
out[parentName] = [arr[i].name]
}
}
}
return out;
};
arras = [{
id: 1,
name: "parent1",
parent: null
},
{
id: 2,
name: "children1",
parent: 1
},
{
id: 3,
name: "children2",
parent: 1
},
{
id: 4,
name: "parent2",
parent: null
},
{
id: 5,
name: "children3",
parent: 4
},
{
id: 6,
name: "children4",
parent: 4
}
]
//console.log(convert(arras, null));
alert(JSON.stringify(convert(arras)));
But notice for multilevel it doesn't work correctly. If your need it, your must save map for all possible parent list

arras.forEach(function(el){
if(el.parent){
el.parent=arras.find(e=>e.id==el.parent)||(console.error("no parent:"+el.parent),undefined);
}
});
//resolved parent/childs....
var newmodel = arras.reduce(function(obj,el){
if(el.parent){
//child
obj[el.parent.name]=obj[el.parent.name]||[];//create new parent if neccessary
obj[el.parent.name].push({name:el.name});
}else{
//parent
obj[el.name]=obj[el.name]||[];
}
return obj;
},{});
http://jsbin.com/renicijufi/edit?console

Another way:
var arrays = [
{id: 1, name: 'parent1', parent: null},
{id: 2, name: 'children1', parent: 1},
{id: 3, name: 'children2', parent: 1},
{id: 4, name: 'parent2', parent: null},
{id: 5, name: 'children3', parent: 4},
{id: 6, name: 'children4', parent: 4}
];
// First, reduce the input arrays to id based map
// This step help easy to select any element by id.
arrays = arrays.reduce(function (map, el) {
map[el.id] = el;
return map;
}, {});
var result = Object.values(arrays).reduce(function (result, el) {
if (!el.parent) {
result[el.name] = [];
} else {
result[arrays[el.parent].name].push(el.name);
}
return result;
}, {});
console.log(result);

I think this meets your requirement
Obj = new Object();
for( i in arras){
person = arras[i];
if(person.parent != null){
if(!Obj.hasOwnProperty(person.parent)){
// here instead of the index you can use Obj["parent"+person.parent] get the exact thing. If you are using that use tha in rest of the code
Obj[person.parent] = new Array();
}
Obj[person.parent].push(person);
}
else{
if(!Obj.hasOwnProperty(person.id)){
// Some parents might have kids not in the list. If you want to ignore, just remove from the else.
Obj[person.id] = new Array()
}
}
}
Edit :
Obj = new Object();
for( i in arras){
person = arras[i];
if(person.parent != null){
if(!Obj.hasOwnProperty(person.parent)){
// here instead of the index you can use Obj["parent"+person.parent] get the exact thing. If you are using that use tha in rest of the code
Obj[person.parent] = new Array();
}
Obj[person.parent].push({name : person.name});
}
else{
if(!Obj.hasOwnProperty(person.id)){
// Some parents might have kids not in the list. If you want to ignore, just remove from the else.
Obj[person.id] = new Array()
}
}
}
Hope this helps. :)

Related

I want to loop over array and make my loop return function if the name does not exist?

I want to loop over the array and make my loop return function or one time string to say the name exist?
this is the error
TypeError: Cannot read property 'name' of undefined
var x = arr[i].name;
var arr = [
{id: 1, name: "php"},
{id: 2, name: "mysql"},
{id: 3, name: "laravel"},
{id: 4, name: "codeigniter"},
{id: 5, name: "wordpress"},
{id: 6, name: "sql"},
{id: 7, name: "jquery"},
{id: 8, name: "javascript"},
];
var string;
function checkemail(arr, string) {
var i;
for (i = 0; i < arr.length; i++) {
let newname = arr[i].name;
if (newname !== string) {
return storename();
} else {
console.log("name found")
}
}
}
}
console.log(checkemail(arr, "javascript"));
You should loop through the entire array before making a decision on the existence of the element in the array
You are calling storename function on the first mismatch of the name and parameter. This will make the function checking the first element and return after calling storename function.
My implementation
function storename() {
return 'storename function';
}
var arr = [
{ id: 1, name: "php" },
{ id: 2, name: "mysql" },
{ id: 3, name: "laravel" },
{ id: 4, name: "codeigniter" },
{ id: 5, name: "wordpress" },
{ id: 6, name: "sql" },
{ id: 7, name: "jquery" },
{ id: 8, name: "javascript" },
];
var string;
function checkemail(arr, string) {
var i;
let isFound = false;
for (i = 0; i < arr.length; i++) {
let newname = arr[i].name;
if (newname === string) {
isFound = true;
i = arr.length; // Item found, you can exit the loop now
return "name found";
}
}
if(!isFound) {
return storename()
}
}
console.log(checkemail(arr, "javascript"));
Array.find implementation:
You can implement the same logic without using a manual loop by using Array.find as below.
var arr = [
{ id: 1, name: "php" },
{ id: 2, name: "mysql" },
{ id: 3, name: "laravel" },
{ id: 4, name: "codeigniter" },
{ id: 5, name: "wordpress" },
{ id: 6, name: "sql" },
{ id: 7, name: "jquery" },
{ id: 8, name: "javascript" },
];
var string;
function storename() {
return 'storename function';
}
function checkemail(arr, string) {
const node = arr.find(item => item.name === string);
return node ? "name found" : storename();
}
console.log(checkemail(arr, "javascript"));

array grouping conversion key start_section to end_section

Let's say I have below array :
[{id: 1, name: "header"},{id: 2, name: "start_section"},
{id: 3, name: "input"}, {id: 5, name: "image"},
{id: 6, name: "end_section"}, {id: 7, name: "header"},
{id: 8, name: "start_section"}, {id: 9, name: "input"},
{id: 10, name: "date"}, {id: 11, name: "end_section"},
]
I want this :
[{
id: 1,
name: "header"
}, {
id: 2,
name: "section",
child: [{
{
id: 3,
name: "input"
},
{
id: 5,
name: "image"
},
}],
}, {
id: 7,
name: "header"
}, {
id: 8,
name: "section",
child: [{
{
id: 9,
name: "input"
},
{
id: 10,
name: "date"
},
}]
}]
if I find start_section and end_section then it will form a new object , How do I change the array by grouping by the key specified in the example above in javascript?
If I get it right, you want something like this? It's simple approach with for loop and some flags:
const arr = [{id: 1, name: "header"},{id: 2, name: "start_section"},
{id: 3, name: "input"}, {id: 5, name: "image"},
{id: 6, name: "end_section"}, {id: 7, name: "header"},
{id: 8, name: "start_section"}, {id: 9, name: "input"},
{id: 10, name: "date"}, {id: 11, name: "end_section"},
];
// Set final array
let finalArray = [];
// Set sub object for groups (Childs)
let subObj = {};
// Flag for sub section stuff
let inSubSection = false;
// Loop array
for(let i = 0; i < arr.length; i++) {
if(arr[i].name === "end_section") {
// If we have end_section
// Set flag off
inSubSection = false;
// Push sub object to final array
finalArray.push(subObj);
} else if(arr[i].name === "start_section") {
// If we get start_section
// Set flag on
inSubSection = true;
// Set new sub object, set childs array in it
subObj = {
id: arr[i].id,
name: "section",
child: []
};
} else if(inSubSection) {
// If we have active flag (true)
// Push child to section array
subObj.child.push({
id: arr[i].id,
name: arr[i].name
});
} else {
// Everything else push straight to final array
finalArray.push(arr[i]);
}
}
// Log
console.log(finalArray);
you can Array.reduce function
let array = [{id: 1, name: "header"},{id: 2, name: "start_section"},
{id: 3, name: "input"}, {id: 5, name: "image"},
{id: 6, name: "end_section"}, {id: 7, name: "header"},
{id: 8, name: "start_section"}, {id: 9, name: "input"},
{id: 10, name: "date"}, {id: 11, name: "end_section"},
]
let outPut = array.reduce( (acc, cur, i, arr) => {
if (cur.name == "start_section") {
//find the end element
let endIndex = arr.slice(i).findIndex( e => e.name == "end_section") + i ;
//splice the child elements from base array
let child = arr.splice(i + 1, endIndex - 1 );
//remove last element that has "end_section"
child.splice(-1);
//append child
cur.child = child;
//sert the name as "section"
cur.name = "section";
}
//add to accumulator
acc.push(cur);
return acc;
}, []);
console.log(outPut);

how to order arrangements based on a condition in javascript?

I have a structure in which the number of arrangements can vary:
array1 = [
{local: {id: 1, name: 'local1'}},
{local: {id: 2, name: 'local2'}},
{local: {id: 3, name: 'local3'}},
{local: {id: 4, name: 'local4'}},
{local: {id: 5, name: 'local5'}}
];
array2 = [
{local: {id: 1, name: 'local1'}},
{local: {id: 3, name: 'local3'}},
{local: {id: 3, name: 'local4'}},
{local: {id: 3, name: 'local5'}},
];
array3 = [
{local: {id: 1, name: 'local1'}},
{local: {id: 3, name: 'local2'}},
{local: {id: 3, name: 'local3'}},
{local: {id: 3, name: 'local5'}},
];
I need to create a new array from these, in which this new array is ordered first by the ids that are repeated in all the arrays and then the ones that are not repeated, should be something like this:
newArray = [
{local: {id: 1, name: 'local1'}},
{local: {id: 3, name: 'local3'}},
{local: {id: 5, name: 'local5'}},
{local: {id: 2, name: 'local2'}},
{local: {id: 4, name: 'local4'}}
]
Someone who can help me please!!
Converting all the arrays to objects for fast searching.
const array1 = [{
local: {
id: 1,
name: 'local1'
}
},
{
local: {
id: 2,
name: 'local2'
}
},
{
local: {
id: 3,
name: 'local3'
}
},
{
local: {
id: 4,
name: 'local4'
}
},
{
local: {
id: 5,
name: 'local5'
}
}
];
const array2 = [{
local: {
id: 1,
name: 'local1'
}
},
{
local: {
id: 3,
name: 'local3'
}
},
{
local: {
id: 3,
name: 'local4'
}
},
{
local: {
id: 3,
name: 'local5'
}
},
];
const array3 = [{
local: {
id: 1,
name: 'local1'
}
},
{
local: {
id: 3,
name: 'local2'
}
},
{
local: {
id: 3,
name: 'local3'
}
},
{
local: {
id: 3,
name: 'local5'
}
},
];
const obj1 = array1.reduce((acc, item) => {
acc[item.local.id] = item;
return acc;
}, {});
const obj2 = array2.reduce((acc, item) => {
acc[item.local.id] = item;
return acc;
}, {});
const obj3 = array3.reduce((acc, item) => {
acc[item.local.id] = item;
return acc;
}, {});
const result = {
...obj3,
...obj2,
...obj1
};
const output = [];
const temp = [];
for (let key in result) {
if (obj1[key] && obj2[key] && obj3[key]) {
output.push(result[key]);
} else temp.push(result[key]);
}
console.log([...output, ...temp]);
I would do it like this (may not be the optimum solution):
/* Same Arrays as yours */ const array1=[{local:{id:1,name:"local1"}},{local:{id:2,name:"local2"}},{local:{id:3,name:"local3"}},{local:{id:4,name:"local4"}},{local:{id:5,name:"local5"}}],array2=[{local:{id:1,name:"local1"}},{local:{id:3,name:"local3"}},{local:{id:3,name:"local4"}},{local:{id:3,name:"local5"}}],array3=[{local:{id:1,name:"local1"}},{local:{id:3,name:"local2"}},{local:{id:3,name:"local3"}},{local:{id:3,name:"local5"}}];
function myFunc(arrays) {
// All items, with duplicates
const allItems = [].concat.apply([], arrays);
// All IDs, without duplicates thanks to `Set`
const allIDs = Array.from(
allItems.reduce((set, item) => set.add(item.local.id), new Set())
);
// Helper function used for sorting
const isInAllArrays = id => arrays.every(
arr => arr.some(item => item.local.id === id)
);
// Sort the IDs based on whether they are in all arrays or not
allIDs.sort((a, b) => {
const _a = isInAllArrays(a), _b = isInAllArrays(b);
if (_a !== _b) return _a ? -1 : 1;
return 0;
});
// Map all IDs to the first element with this ID
return allIDs.map(id => allItems.find(item => item.local.id === id));
}
const newArray = myFunc([array1, array2, array3]);
// Just for readability in the demo below
console.log(JSON.stringify(newArray).split('},{').join('},\n{'));
1) Traverse all arrays and build an object with keys as id and value include object and also maintain the frequency of occurrence (count).
2) Now, Object.values of above object and sort them based on 'count'.
You will get most frequent items at top.
const sort = (...arrs) => {
const all = {};
arrs
.flat()
.forEach(
(obj) =>
(all[obj.local.id] =
obj.local.id in all
? { ...all[obj.local.id], count: all[obj.local.id].count + 1 }
: { ...obj, count: 1 })
);
return Object.values(all)
.sort((a, b) => b.count - a.count)
.map(({ count, ...rest }) => rest);
};
array1 = [
{ local: { id: 1, name: "local1" } },
{ local: { id: 2, name: "local2" } },
{ local: { id: 3, name: "local3" } },
{ local: { id: 4, name: "local4" } },
{ local: { id: 5, name: "local5" } },
];
array2 = [
{ local: { id: 1, name: "local1" } },
{ local: { id: 3, name: "local3" } },
{ local: { id: 3, name: "local4" } },
{ local: { id: 3, name: "local5" } },
];
array3 = [
{ local: { id: 1, name: "local1" } },
{ local: { id: 3, name: "local2" } },
{ local: { id: 3, name: "local3" } },
{ local: { id: 3, name: "local5" } },
];
console.log(sort(array1, array2, array3))

compare two arrays in javascript and delete the object that both arrays have

I have 2 arrays:
0: {id: 2, name: "TMA"}
1: {id: 3, name: "Hibbernate"}
0: {id: 1, name: "FB.DE"}
1: {id: 2, name: "TMA"}
2: {id: 3, name: "Hibbernate"}
3: {id: 4, name: "Event.it A"}
4: {id: 5, name: "Projket 2"}
5: {id: 6, name: "Projekt 1"}
I want to compare them and delete the objects with the id 2 and 3 cause both arrays have them and thats the similarity.
This is my Code so far:
const projectListOutput = projectsOfPersonArray.filter(project => data.includes(project));
console.log(projectListOutput);
But every time i run this projectListOutput is empty.
When using includes dont compare objects, Just build data as array of strings. Remaining code is similar to what you have.
arr1 = [
{ id: 2, name: "TMA" },
{ id: 3, name: "Hibbernate" },
];
arr2 = [
{ id: 1, name: "FB.DE" },
{ id: 2, name: "TMA" },
{ id: 3, name: "Hibbernate" },
{ id: 4, name: "Event.it A" },
{ id: 5, name: "Projket 2" },
{ id: 6, name: "Projekt 1" },
];
const data = arr1.map(({ id }) => id);
const result = arr2.filter(({ id }) => !data.includes(id));
console.log(result);
Your data array probably does not contain the exact same object references than projectsOfPersonArray. Look at the code below:
[{ foo: 'bar' }].includes({ foo: 'bar' });
// false
Objects look equal, but they don't share the same reference (= they're not the same).
It's safer to use includes with primitive values like numbers or strings. You can for example check the ids of your objects instead of the full objects.
You compare different objects, so every object is unique.
For filtering, you need to compare all properties or use a JSON string, if the order of properties is equal.
var exclude = [{ id: 2, name: "TMA" }, { id: 3, name: "Hibbernate" }],
data = [{ id: 2, name: "TMA" }, { id: 3, name: "Hibbernate" }, { id: 1, name: "FB.DE" }, { id: 2, name: "TMA" }, { id: 3, name: "Hibbernate" }, { id: 4, name: "Event.it A" }, { id: 5, name: "Projket 2" }, { id: 6, name: "Projekt 1" }],
result = data.filter(project =>
!exclude.some(item => JSON.stringify(item) === JSON.stringify(project))
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can do something similar to the next:
const source = [{
id: 1,
name: "FB.DE"
},
{
id: 2,
name: "TMA"
},
{
id: 3,
name: "Hibbernate"
},
{
id: 4,
name: "Event.it A"
},
{
id: 5,
name: "Projket 2"
},
{
id: 6,
name: "Projekt 1"
}
]
const toRemove = [{
id: 2,
name: "TMA"
},
{
id: 3,
name: "Hibbernate"
}
]
/**create object where keys is object "id" prop, and value is true**/
const toRemoveMap = toRemove.reduce((result, item) => ({
...result,
[item.id]: true
}), {})
const result = source.filter(item => !toRemoveMap[item.id])
You can make function from it:
function removeArrayDuplicates (sourceArray, duplicatesArray, accessor) {
const toRemoveMap = duplicatesArray.reduce((result, item) => ({
...result,
[item[accessor]]: true
}), {});
return sourceArray.filter(item => !toRemoveMap[item[accessor]])
}
removeArrayDuplicates(source, toRemove, 'id')
Or even better, you can make it work with a function instead of just property accessor:
function removeDuplicates (sourceArray, duplicatesArray, accessor) {
let objectSerializer = obj => obj[accessor];
if(typeof accessor === 'function') {
objectSerializer = accessor;
}
const toRemoveMap = duplicatesArray.reduce((result, item) => ({
...result,
[objectSerializer(item)]: true
}), {});
return sourceArray.filter(item => !toRemoveMap[objectSerializer(item)])
}
removeDuplicates(source, toRemove, (obj) => JSON.stringify(obj))
This function will help you merge two sorted arrays
var arr1 = [
{ id: 2, name: 'TMA' },
{ id: 3, name: 'Hibbernate' },
]
var arr2 = [
{ id: 1, name: 'FB.DE' },
{ id: 2, name: 'TMA' },
{ id: 3, name: 'Hibbernate' },
{ id: 4, name: 'Event.it A' },
{ id: 5, name: 'Projket 2' },
]
function mergeArray(array1, array2) {
var result = []
var firstArrayLen = array1.length
var secondArrayLen = array2.length
var i = 0 // index for first array
var j = 0 // index for second array
while (i < firstArrayLen || j < secondArrayLen) {
if (i === firstArrayLen) { // first array doesn't have any other members
while (j < secondArrayLen) { // we copy rest members of first array as a result
result.push(array2[j])
j++
}
} else if (j === secondArrayLen) { // second array doesn't have any other members
while (i < firstArrayLen) { // we copy the rest members of the first array to the result array
result.push(array1[i])
i++
}
} else if (array1[i].id < array2[j].id) {
result.push(array1[i])
i++
} else if (array1[i].id > array2[j].id) {
result.push(array2[j])
j++
} else {
result.push(array1[i])
i++
j++
}
}
return result
}
console.log(mergeArray(arr1,arr2));

Efficient way of writing child of parents in javascript

var array = [
{id: 1, name: "Father", parent_id: null},
{id: 2, name: "Child", parent_id: 1},
{id: 3, name: "Child", parent_id: 1},
{id: 4, name: "ChildChild", parent_id: 2},
{id: 5, name: "ChildChildChild", parent_id: 4}
]
for(var i in array){
if(array[i].parent_id == null){
console.log(array[i].name);
} else {
for(var j in array){
if(array[i].parent_id == array[j].id && array[j].parent_id == null){
console.log(">" + array[i].name);
for(var x in array){
if(array[i].id == array[x].parent_id){
console.log(">>" + array[x].name);
}
}
}
}
}
}
Output:
Father
>Child
>>ChildChild
>Child
I have this array which has id, name and parent_id. Right now it is fixed but it could have multiple arrays and can be nested for n amount of times.
What I am doing here is iterating through each array and trying to find which are the parents and which one is the child.
I want to know if there is a more efficient way to write this code. For instance, I added a fifth id but that would require another for loop and so on. The output would be the same just a printed out tree.
You can use a Map to key your nodes by id, and then use recursion to traverse them in depth first order:
var array = [{id: 1, name: "Father", parent_id: null},{id: 2, name: "Child", parent_id: 1},{id: 3, name: "Child", parent_id: 1},{id: 4, name: "ChildChild", parent_id: 2},{id: 5, name: "ChildChildChild", parent_id: 4}];
let map = new Map(array.map(({id}) => [id, []])).set(null, []);
array.forEach(node => map.get(node.parent_id).push(node));
function dfs(nodes, indent="") {
for (let node of nodes) {
console.log(indent + node.name);
dfs(map.get(node.id), indent+">");
}
}
dfs(map.get(null));
You could create a tree and then make the output.
const
print = ({ name, children = [] }) => {
console.log(name)
children.forEach(print);
},
array = [{ id: 1, name: "Father", parent_id: null }, { id: 2, name: "Child", parent_id: 1 }, { id: 3, name: "Child", parent_id: 1 }, { id: 4, name: "ChildChild", parent_id: 2 }, { id: 5, name: "ChildChildChild", parent_id: 4 }],
tree = function (data, root) {
var t = {};
data.forEach(o => {
Object.assign(t[o.id] = t[o.id] || {}, o);
t[o.parent_id] = t[o.parent_id] || {};
t[o.parent_id].children = t[o.parent_id].children || [];
t[o.parent_id].children.push(t[o.id]);
});
return t[root].children;
}(array, null);
tree.forEach(print);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories