Flat array to multi dimensional array (JavaScript) - javascript

I have the following array:
var sampleArray = [
"CONTAINER",
"BODY",
"NEWS",
"TITLE"];
I want to have the following output:
var desiredOutput = [{
"CONTAINER": [{
"BODY": [{
"NEWS": [{
"TITLE": []
}]
}]
}]
}];
How can I achieve this in JavaScript?
Already tried with recursive loop, but it does not work, gives me undefined.
dataChange(sampleArray);
function dataChange(data) {
for (var i = 0; i < data.length; i++) {
changeTheArray[data[i]] = data[i + 1];
data.splice(i, 1);
dataChange(changeTheArray[data[i]]);
}
}
Thanks

This does what you're asking for, in one line, and with no additional variables:
let desiredOutput = sampleArray.reduceRight((obj, key) => [ { [key]: obj } ], []);
The reduceRight call, starting from the right hand end of the array, progressively accumulates the current data (seeded with the initial value of []) as the value of the single key in a new object { [key] : _value_ } where that object is itself the single entry in an array [ ... ].

This will do it:
const sampleArray = ["CONTAINER", "BODY", "NEWS", "TITLE"];
const data = []; // Starting element.
let current = data; // Pointer to the current element in the loop
sampleArray.forEach(key => { // For every entry, named `key` in `sampleArray`,
const next = []; // New array
current.push({[key]: next}); // Add `{key: []}` to the current array,
current = next; // Move the pointer to the array we just added.
});
console.log(data);
{[key]: next} is relatively new syntax. They're computed property names.
This:
const a = 'foo';
const b = {[a]: 'bar'};
Is similar to:
const a = 'foo';
const b = {};
b[a] = 'bar';
You could re-write the forEach as a one-liner:
const sampleArray = ["CONTAINER", "BODY", "NEWS", "TITLE"];
const data = []; // Starting element.
let current = data; // Pointer to the current element in the loop
sampleArray.forEach(key => current.push({[key]: current = [] }));
console.log(data);
This current.push works a little counter-intuitively:
Construct a new element to push. This assigns a new value to current.
Push the new element to the reference .push was called on.
That reference is the value of current before current = [].

Hi i made a little demo :
var sampleArray = [
"CONTAINER",
"BODY",
"NEWS",
"TITLE"
],
generateArray = [],
tmp = null;
for(var i = 0; i < sampleArray.length; i++) {
if(tmp===null){
generateArray[sampleArray[i]] = {};
tmp = generateArray[sampleArray[i]];
}else{
tmp[sampleArray[i]] = {};
tmp = tmp[sampleArray[i]];
}
}
console.log(generateArray);

Related

How do i add a key value pair to an asociative array in javascript

let finaloutput = {};
$.each( arr, function(index,key) {
let outarray = [];
// var id sourced from another function
outarray.push(id,index,key);
//??finaloutput[id].push(outarray);
}
In the above code i am trying to store an object similar to below.
Each time the loop grabs the same id it appends the array in the finaloutput object
eg
id:index:key
1st loop where id = 7 index=param 1 key=val1
{"7":[{param1:val1}]}
2nd .. id = 7 index=param 2 key=val2
{"7":[{param1:val1,param2:val2}]}
3rd ... id = 8 index=param 1 key=val1
{"7":[{param1:val1,param2:val2}],"8":[{param1:val1}]}
How do i achieve this
I tried to generated similar output using sample data:
`let indx = ["param1", "param2", "param3", "param4", "param5"]
let key = ["k1", "k2", "k3", "k4", "k5"]
let id = ["1", "2", "3", "4", "5"]
let resultObj = {}
for (let i = 0; i < 5; i++) {
if (resultObj[id]) {
let temp=Object.assign({}, {[indx[i]]:key[i]}, ...resultObj[id[i]])
resultObj[id[i]] = [temp];
}
else{
let ob=[{[indx[i]]:key[i]}]
resultObj[id[i]]=ob
}
}
console.log(resultObj)`
In your case you can do something like :
let finaloutput = {};
$.each(arr, function (index, key) {
if (finaloutput[id]) {
let temp = Object.assign({}, { [index]: key }, ...finaloutput[id])
finaloutput[id] = [temp];
}
else {
let temp2 = [{ [index]: key }]
finaloutput[id] = temp2
}
}
Note Please refer to my example to get better understanding incase I was not able to exactly formulate answer of your code or it gives error
You have an object finaloutput. So your goal can be divided into smaller pieces:
just create a key in object
assign an array to the above key
push desired values into array
So the code should look like this:
let finaloutput = {};
$.each( arr, function(index,key) {
finaloutput[key] = [];
let outarray = [];
// var id sourced from another function
finaloutput[key].push({id:key});
}
Or it can be done using reduce() method. Let me show an example::
const array = ["one", "two", "three", "one", "one", "two", "two", "three", "four", "five"];
const result = array.reduce((a, c) => {
a[c]=a[c] || [];
a[c].push({yourKey: c});
return a;
}, {})
console.log(result);

combine multiple list to single array

{ "data": [ {"firstName": "Achmad"}, {"lastName": "a"} ] } and this my script var body = request.body;for(var i = 0;i < body.data.length;i++){var obj = body.data[i];var keyes = Object.keys(obj);} the problem response from var keyes = Object.keys(obj); is list like this [ 'firstName' ] [ 'lastName' ] i'm wanna like this ['firstName', 'lastName']
Thanks before.
Assuming each of the arrays are elements of a parent array, one way you could achieve this is by using Array.prototype.reduce:
const flat = [
["aku"],
["dia"],
["ia"]
].reduce((accum, el) => accum.concat(el), [])
console.log(flat);
EDITED: You could concat each item of your array :
const body = {
"data": [
{"firstName": "Achmad"},
{"lastName": "a"}
]
};
let result = [];
for (item of body.data) {
result = result.concat(Object.keys(item));
}
console.log(result); // -> ['firstName', 'lastName']
Maybe you want to do something like this
var body = request.body;
var keyes = [];
for(var i = 0; i < body.data.length; i++){
var obj = body.data[i];
keyes.push( Object.keys(obj) );
}

Create dynamic object based on array element: Javascript [duplicate]

I have the following array:
var sampleArray = [
"CONTAINER",
"BODY",
"NEWS",
"TITLE"];
I want to have the following output:
var desiredOutput = [{
"CONTAINER": [{
"BODY": [{
"NEWS": [{
"TITLE": []
}]
}]
}]
}];
How can I achieve this in JavaScript?
Already tried with recursive loop, but it does not work, gives me undefined.
dataChange(sampleArray);
function dataChange(data) {
for (var i = 0; i < data.length; i++) {
changeTheArray[data[i]] = data[i + 1];
data.splice(i, 1);
dataChange(changeTheArray[data[i]]);
}
}
Thanks
This does what you're asking for, in one line, and with no additional variables:
let desiredOutput = sampleArray.reduceRight((obj, key) => [ { [key]: obj } ], []);
The reduceRight call, starting from the right hand end of the array, progressively accumulates the current data (seeded with the initial value of []) as the value of the single key in a new object { [key] : _value_ } where that object is itself the single entry in an array [ ... ].
This will do it:
const sampleArray = ["CONTAINER", "BODY", "NEWS", "TITLE"];
const data = []; // Starting element.
let current = data; // Pointer to the current element in the loop
sampleArray.forEach(key => { // For every entry, named `key` in `sampleArray`,
const next = []; // New array
current.push({[key]: next}); // Add `{key: []}` to the current array,
current = next; // Move the pointer to the array we just added.
});
console.log(data);
{[key]: next} is relatively new syntax. They're computed property names.
This:
const a = 'foo';
const b = {[a]: 'bar'};
Is similar to:
const a = 'foo';
const b = {};
b[a] = 'bar';
You could re-write the forEach as a one-liner:
const sampleArray = ["CONTAINER", "BODY", "NEWS", "TITLE"];
const data = []; // Starting element.
let current = data; // Pointer to the current element in the loop
sampleArray.forEach(key => current.push({[key]: current = [] }));
console.log(data);
This current.push works a little counter-intuitively:
Construct a new element to push. This assigns a new value to current.
Push the new element to the reference .push was called on.
That reference is the value of current before current = [].
Hi i made a little demo :
var sampleArray = [
"CONTAINER",
"BODY",
"NEWS",
"TITLE"
],
generateArray = [],
tmp = null;
for(var i = 0; i < sampleArray.length; i++) {
if(tmp===null){
generateArray[sampleArray[i]] = {};
tmp = generateArray[sampleArray[i]];
}else{
tmp[sampleArray[i]] = {};
tmp = tmp[sampleArray[i]];
}
}
console.log(generateArray);

How to convert an array into a hierarchical array

I have some data which is
var currentData = [
{'ticket':'CAP', 'child':'CT-1'},
{'ticket':'CAP', 'child':'CT-2'},
{'ticket':'CT-1', 'child':'CT-1-A'},
{'ticket':'CT-1', 'child':'CT-1-B'}
];
The data is flat and I need to convert it into something like:
{
'ticket': 'CAP',
children : [{
'ticket' : 'CT-1',
'children' : [{
'ticket' : 'CT-1-A',
'children' : []
}, {
'ticket' : 'CT-1-B',
'children' : []
}],
[{
'ticket' : 'CT-2',
'children' : []
}]
}]
}
(I think the above is valid)?
I'm very lost as to how. I am going to show my effort but, I'm not sure if my approach is correct or not.
var currentData = [{'ticket':'cap', 'child':'CT-1'},{'ticket':'cap', 'child':'CT-2'}, {'ticket':'CT-1', 'child':'CT-1-A'},{'ticket':'CT-1', 'child':'CT-1-B'}];
var newList = [];
function convert(list){
if (newList.length <= 0){
var child = [];
var emptyChild = [];
child.push({'ticket': list[0].child, 'child': emptyChild });
newList.push({'ticket': list[0].ticket, 'children' : child});
list.splice(0,1);
} // the if statement above works fine
for(var i = 0; i < list.length; i++) {
var ticket = list[i].ticket;
for(var j = 0; j < newList.length; j++) {
if (newList[j].ticket == ticket){
var child;
var emptyChild = [];
child = {'ticket': list[i].child, 'child': emptyChild };
newList[j].children.push(child);
list.splice(i,1);
break;
} // the if above works
else{
var child2 = getFromChildren(ticket, newList, list[i]); // child2 is Always null, even if getFromChildren returns an object
newList[j].children.push(child2);
list.splice(i,1);
break;
}
}
}
if (list.length > 0){
convert(list);
}
}
function getFromChildren(ticket, list, itemToAdd){
if (list == null || list[0].children == null)
return;
for(var i = 0; i < list.length; i++) {
if (list[i] == null)
return;
if (list[i].ticket == ticket){
list[i].child.push(itemToAdd.child); // ** can't do this, javascript passes by value, not by reference :(
} else{
getFromChildren(ticket, list[i].children, itemToAdd);
}
}
}
convert(currentData);
I think I've made a mess of it. In the comments I've put a ** explaining that it isn't working due to JavaScript not passing by reference, however upon further reading I don't think that is correct as I'm passing the object which is by reference?
Edit
The data, shown with currentData will not always start at the root sadly either
function convert(arr) {
var children = {}; // this object will hold a reference to all children arrays
var res = arr.reduce(function(res, o) { // for each object o in the array arr
if(!res[o.ticket]) { // if there is no object for the element o.ticket
res[o.ticket] = {ticket: o.ticket, children: []}; // then creates an object for it
children[o.ticket] = res[o.ticket].children; // and store a reference to its children array
}
if(!res[o.child]) { // if there is no object for the element o.child
res[o.child] = {ticket: o.child, children: []}; // then creates an object for it
children[o.child] = res[o.child].children; // and store a reference to its children array
}
return res;
}, {});
arr.forEach(function(o) { // now for each object o in the array arr
children[o.ticket].push(res[o.child]); // add the object of o.child (from res) to its children array
delete res[o.child]; // and remove the child object from the object res
});
return res;
}
var currentData = [
{'ticket':'CAP', 'child':'CT-1'},
{'ticket':'CAP', 'child':'CT-2'},
{'ticket':'CT-1', 'child':'CT-1-A'},
{'ticket':'CT-1', 'child':'CT-1-B'}
];
console.log(convert(currentData));
Explanation:
The reduce part creates an object of the form: { ticket: "...", children: [] } for each element (child or not). So right after reduce, the object res will be:
res = {
'CAP': { ticket: 'CAP', children: [] },
'CT-1': { ticket: 'CT-1', children: [] },
'CT-2': { ticket: 'CT-2', children: [] },
'CT-1-A': { ticket: 'CT-1-A', children: [] },
'CT-1-B': { ticket: 'CT-1-B', children: [] },
}
Now comes the forEach bit which loops over the array once more, and now for each object it fetches the object of .child from res above, push it into .ticket object's children (which a reference to it is stored in children object), then remove the .child object from the object res.
Below uses reduce to get the data grouped to a Map, then I convert the data to an object like you've shown above. You'll need a modern browser to run below snippet, or use a transpiler like babeljs to convert it to es5 syntax.
let currentData = [
{'ticket':'CAP', 'child':'CT-1'},
{'ticket':'CAP', 'child':'CT-2'},
{'ticket':'CT-1', 'child':'CT-1-A'},
{'ticket':'CT-1', 'child':'CT-1-B'}
];
let children = currentData.map(e => e.child);
currentData.sort((a,b) => children.indexOf(a.ticket));
let res = currentData.reduce((a,b) => {
if (! children.includes(b.ticket)) {
return a.set(b.ticket, (a.get(b.ticket) || [])
.concat({ticket: b.child,
children: currentData
.filter(el => el.ticket === b.child)
.map(el => ({ticket: el.child, children: []}))}))
}
return a;
}, new Map);
let r = {};
for (let [key,value] of res.entries()) {
r.ticket = key;
r.children = value;
}
console.log(r);
Solution using recursion, starting node can be changed.
var currentData = [{'ticket': 'cap','child': 'CT-1'}, {'ticket': 'cap','child': 'CT-2'}, {'ticket': 'CT-1','child': 'CT-1-A'}, {'ticket': 'CT-1','child': 'CT-1-B'}];
function convert(data, start){
return {
ticket: start,
childs: data.filter(d => d.ticket == start)
.reduce((curr, next) => curr.concat([next.child]), [])
.map(c => convert(data, c))
}
}
let result = convert(currentData, 'cap');
console.log(result);
.as-console-wrapper{top: 0; max-height: none!important;}
I would go with a simple for approach, like this:
var currentData = [
{'ticket':'CAP', 'child':'CT-1'},
{'ticket':'CAP', 'child':'CT-2'},
{'ticket':'CT-1', 'child':'CT-1-A'},
{'ticket':'CT-1', 'child':'CT-1-B'}
];
var leafs = {};
var roots = {};
var tickets = {};
for(var i=0; i<currentData.length; i++){
var ticket = currentData[i].ticket;
var child = currentData[i].child;
if(!tickets[ticket]){
tickets[ticket] = {ticket:ticket,children:[]};
if(!leafs[ticket]){
roots[ticket] = true;
}
}
if(!tickets[child]){
tickets[child] = {ticket:child,children:[]};
}
delete roots[child];
leafs[child] = true;
tickets[ticket].children.push(tickets[child]);
}
for(var ticket in roots){
console.log(tickets[ticket]);
}
Well, if you are not familiar with reduce, map , forEach with callbacks to iterate, then here is a approach I came with, where the code is flat, storing object references in another map object and iterating exactly once the source array.
The code is much cleaner, if something is understandable add comments I will explain;
var currentData = [
{'ticket':'CT-1', 'child':'CT-1-A'},
{'ticket':'CT-1', 'child':'CT-1-B'},
{'ticket':'CAP', 'child':'CT-1'},
{'ticket':'CAP', 'child':'CT-2'}
];
function buildHierarchy(flatArr) {
let root = {},
nonRoot = {},
tempMap = {};
Object.setPrototypeOf(root, nonRoot);
for (let idx = 0; idx < flatArr.length; idx++) {
let currTicket = flatArr[idx];
let tempTicket = tempMap[currTicket.ticket] || {ticket: currTicket.ticket, children: []};
tempMap[currTicket.ticket] = tempTicket;
if (currTicket.child) {
let tempChild = tempMap[currTicket.child] || {ticket: currTicket.child, children: []};
tempTicket.children.push(tempChild);
tempMap[currTicket.child] = tempChild;
delete root[tempChild.ticket];
nonRoot[tempChild.ticket] = true;
}
root[tempTicket.ticket] = true;
}
return tempMap[Object.keys(root)[0]];
}
console.log(buildHierarchy(currentData));
I have changed the sequence of your source array in order to put the root object anywhere, and the code should work on that.

How to sort 2nd array by sorting the 1st one using Javascript or JQuery?

I have 2 array. I want to sort only first array and the second array should be shorted on the base of first array.
Before sorting:
arrTexts = ["Company", "Department", "Account"];
arrValue = ["nameCompany", "department", "account"];
Should be like this after shorting:
arrTexts = ["Account", "Company", "Department"];
arrValue = ["account", "nameCompany", "department"];
I know sorting by arrTexts.sort(); But it is not so useful in above case.
Please not array length can be 300+. So, performance(sorting speed) also maters.
Can anybody please suggest me?
Here is some code to get started.
var arrTexts = [
"Company", "Department", "Account"
];
var arrValue = [
"nameCompany", "department", "account"
];
var sortedArr1 = arrTexts.sort();
var sortedArr2 = [];
sortedArr1.forEach(function(v, i) {
var t = arrValue.find(function(_v) {
return _v.toLowerCase().indexOf(v.toLowerCase()) > -1;
});
sortedArr2.push(t);
});
console.log(sortedArr1);
console.log(sortedArr2);
Here is a way to do it :
arrTexts = ["Company", "Department", "Account"];
arrValues = ["nameCompany", "department", "account"];
arrTextsValues = {};
arrTexts.forEach(function(item, key){
arrTextsValues[item] = arrValues[key];
})
arrTextsSorted = arrTexts.sort();
arrValuesSorted = [];
arrTextsSorted.forEach(function(item){
arrValuesSorted.push(arrTextsValues[item]);
})
console.log(arrTextsSorted, arrValuesSorted);
It outputs this :
[ 'Account', 'Company', 'Department' ]
[ 'account', 'nameCompany', 'department' ]
First I create an object that will hold a correspondance between texts and values, then I sort the texts and finally, I loop over the sorted texts to create an array holding the values in the correct order based of the correspondance object created earlier.
Hope this helps.
I don't know if it will suits your needs regarding performance issues. That's yours to find out :)
var arrTexts = ["Company", "Department", "Account"];
var arrValue = ["nameCompany", "department", "account"];
// temporary array holds objects with position and sort-value
var mapped = arrTexts.map(function(el, i) {
return { index: i, value: el };
})
// sorting the mapped array containing the reduced values
mapped.sort(function(a, b) {
return +(a.value > b.value) || +(a.value === b.value) - 1;
});
// container for the resulting order
var result = mapped.map(function(el){
return arrValue[el.index];
});
console.log(result);
You could use the indices as a temporary array and sort it with the values of arrTexts. Then map the result to arrValue.
var arrTexts = ["Company", "Department", "Account"],
arrValue = ["nameCompany", "department", "account"],
indices = arrTexts.map(function (_, i) { return i; }),
values,
texts;
indices.sort(function (a, b) { return arrTexts[a].localeCompare(arrTexts[b]); });
texts = indices.map(function (i) { return arrTexts[i]; });
values = indices.map(function (i) { return arrValue[i]; });
console.log(values);
console.log(texts);

Categories

Resources