Flatten a nested objects to one dimensional array javascript - javascript

I have a nested objects in this structure:
myArray = {
"D": {
"U": {
"A300": "B300",
"A326": "B326",
"A344": "B344",
"A345": "B345"
},
"P": {
"A664": "B664",
"A756": "B756"
}
},
"I": {
"U": {
"A300": "B300",
"A326": "B326"
},
"P": {
"A756": "B756"
}
}
};
I am trying to get the data out of it to be only one dimensional (Flatten). I tried the code below but it doesn't work:
var myNewArray = [].concat.apply([], myArray);
and
var myNewArray = myArray.reduce(function(prev, curr) {
return prev.concat(curr);
});
I want myNewArray to have ["B300","B326","B344","B345","B664","B756"]

You can do something like this:
var myArray = [];
myArray[0] = [];
myArray[0][0] = [];
myArray[0][0][0] = [];
myArray[0][0][1] = [];
myArray[0][1] = [];
myArray[0][1][0] = [];
myArray[0][0][0][0] = "abc1";
myArray[0][0][0][1] = "abc2";
myArray[0][0][1][0] = "abc3";
myArray[0][1][0][1] = "abc4";
myArray[0][1][0][1] = "abc5";
function flat(acc, val){
if(Array.isArray(val)){
acc = acc.concat(val.reduce(flat, []));
}else{
acc.push(val);
}
return acc;
}
var newMyArray = myArray.reduce(flat, []);
console.log(newMyArray);
What this does is to recursively reduce all the inner values that are arrays.
It seems that you're dealing with an object. The previous title of your question and the name of the variable are misleading.
In any case, flattening an object is a very similar process.
var myArray = {"D":{"U":{"A300":"B300","A326":"B326","A344":"B344","A345":"B345"},"P":{"A664":"B664","A756":"B756"}},"I":{"U":{"A300":"B300","A326":"B326"},"P":{"A756":"B756"}}};
function flatObj(obj){
return Object.keys(obj).reduce(function(acc, key){
if(typeof obj[key] === "object"){
acc = acc.concat(flatObj(obj[key]));
}else{
acc.push(obj[key]);
}
return acc;
}, []);
}
var newMyArray = flatObj(myArray);
console.log(newMyArray);

I just wanted to add my 2 cents since I was following this question and working on an answer before I left work. I'm home now so I want to post what I came up with.
const obj = {
x1: {
y1: {
z1: {
h1: 'abc',
h2: 'def'
},
z2: {
h1: 123,
h2: 456
}
}
}
}
const valAll = getPropValuesAll(obj)
console.log(valAll)
function getPropValuesAll(obj, result = []){
for(let k in obj){
if(typeof obj[k] !== 'object'){
result.push(obj[k])
continue
}
getPropValuesAll(obj[k], result)
}
return result
}

It would be easy and safe answer.
var myArray = [["abc1"],[["abc2",,"abc3"]],"abc4",{"r5": "abc5", "r6": "abc6"}];
var myNewArray = [];
function flatten(arr){
if(Array.isArray(arr)){
for(var i = 0, l = arr.length; i < l; ++i){
if(arr[i] !== undefined){
flatten(arr[i])
}
}
} else if (typeof arr === 'object') {
for(var key in arr){
if(arr.hasOwnProperty(key)){
flatten(arr[key])
}
}
} else {
myNewArray.push(arr)
}
}
flatten(myArray)
console.log(myNewArray)

Related

How can i get key of nested object = used For....in?

Here is my code. How can i get the key of the key-value pair using for loop?
var apartment = {
bedroom: {
area: 20,
bed: {
type: 'twin-bed',
price: 100
}
}
};
The desired output is as follows:
/* desired results :
* bedroom
* area
* bed
* type
* price
*/
Please help
var getKeys = function(obj) {
var keys = [];
Object.keys(obj).forEach(function(key){
keys.push(key);
if(typeof obj[key] == 'object'){
keys = keys.concat(getKeys(obj[key]));
}
})
return keys;
}
Then
var keys = getKeys(apartment);
You can use a simple Regex as follow:
var apartment = {
bedroom: {
area: 20,
bed: {
type: 'twin-bed',
price: 100
}
}
};
let result = [];
let jsonstr = JSON.stringify(apartment);
// {"bedroom":{"area":20,"bed":{"type":"twin-bed","price":100}}}
let regex = /"(\w+)":/g;
jsonstr.replace(regex, function(match,prop){
result.push(prop);
});
console.log(result);
we can easily done by using regex, convert object string and apply regex to extract the particular word
run the snippet for required output
var apartment = {
bedroom: {
area: 20,
bed: {
type: 'twin-bed',
price: 100
}
}
};
apartment = JSON.stringify(apartment);
var re = /(")\w+(")(:)/g;
var match;
do {
match = re.exec(apartment);
if (match) {
console.log(match[0]);
}
} while (match);
regex : /(")\w+(")(:)/g
only extracts key for more click here
do while loop responsible to detect multiple match in the string
You can use a recursive function :
function getKeys(source, dest) {
for (let key in source) {
if (typeof source[key] == 'object') {
dest.push(key)
getKeys(source[key], dest)
} else {
dest.push(key)
}
}
return dest
}
result = []
const apartment = {
bedroom: {
area: 20,
bed: {
type: 'twin-bed',
price: 100
}
}
}
getKeys(apartment, result) // ["bedroom", "area", "bed", "type", "price"]
var inputs = [
{a:1,b:2,c:3}, // Simple object
{a:{b:2,c:3}}, // Simple object with nesting
{a:{a:{b:2,c:3}}}, // Repeated key hiding nesting
{a:[{b:2,c:3}]}, // keys behind array
];
inputs.push(inputs); // reference cycle and array at top
function getKeys(obj) {
var all = {};
var seen = [];
checkValue(obj);
return Object.keys(all);
function checkValue(value) {
if (Array.isArray(value)) return checkArray(value);
if (value instanceof Object) return checkObject(value);
}
function checkArray(array) {
if (seen.indexOf(array) >= 0) return;
seen.push(array);
for (var i = 0, l = array.length; i < l; i++) {
checkValue(array[i]);
}
}
function checkObject(obj) {
if (seen.indexOf(obj) >= 0) return;
seen.push(obj);
var keys = Object.keys(obj);
for (var i = 0, l = keys.length; i < l; i++) {
var key = keys[i];
all[key] = true;
checkValue(obj[key]);
}
}
}
var result = inputs.map(getKeys);
console.log(result);

How do I write a function that takes an array of values and returns an object

in this format; sample input: ["book", 1, "table", 4] sample output: { string: ["book", "table"], number: [1,4] }
here is the code i have written but it's not giving me the output i want.
function listDic(arr) {
if (Array.isArray(arr)) {
let output = {};
for (let i =0; i == arr.length; i++) {
if (typeof arr[i] === 'string') {
var str = [];
str.push(arr[i]);
}
if (typeof arr[i] === 'number') {
var num = [];
num.push(arr[i]);
}
}
return {string: str, number: num}
}
return "Only arrays are allowed.";
}
please what exactly am i getting wrong?
You could use the type directly as key for the object, while iterating the array.
If you have not an object with the key, generate a new one with an empty array for pushing the actual item.
var array = ["book", 1, "table", 4],
object = {};
array.forEach(function (a) {
var type = typeof a;
object[type] = object[type] || [];
object[type].push(a);
});
console.log(object);
You're overwriting str and num in the loop without ever keeping any other reference to the arrays. Put those arrays in the output object, and then push into those.
var data = ["book", 1, "table", 4];
console.log(listDic(data));
function listDic(arr) {
if (!Array.isArray(arr)) {
return "Only arrays are allowed.";
}
let output = {
string: [],
number: []
};
for (let i = 0; i < arr.length; i++) {
switch (typeof arr[i]) {
case "string":
output.string.push(arr[i]);
break;
case 'number':
output.number.push(arr[i]);
}
}
return output;
}
You could just reduce the array into an object, and use the type of the values as keys
var arr = ["book", 1, "table", 4];
function listDic(arr) {
return arr.reduce((a, b) => {
let t = typeof b;
a[t] ? a[t].push(b) : a[t] = [b];
return a;
}, {});
}
console.log(listDic(arr));
thanks i got a better way to solve it myself
function listDic(arr) {
if (Array.isArray(arr)) {
let str = [];
let num = [];
for (let i =0; i < arr.length; i++) {
if (typeof arr[i] === 'string') {
str.push(arr[i]);
}
if (typeof arr[i] === 'number') {
num.push(arr[i]);
}
}
return {string: str, number: num}
}
return "Only arrays are allowed.";
}
I think this should work for you :
function listDic(arr) {
if (Array.isArray(arr)) {
let output = {};
var str = [],
num = []; //CORRECTED
for (let i = 0; i < arr.length; i++) { //CORRECTED
if (typeof arr[i] === 'string') {
str.push(arr[i]);
}
if (typeof arr[i] === 'number') {
num.push(arr[i]);
}
}
return { string: str, number: num }
}
return "Only arrays are allowed.";
}
console.log(listDic(["book", 1, "table", 4]));
Explanation
Basically, there were two issues:
you were really not entering the loop with == arr.length on your for-loop's entry condition.
you kept re-assigning the str and num arrays with a re-declaration var str[i] = [] OR var num[i] = [].
Hope this helps.

Generate a nested object structure by string key/path

I want to make a function called createAssociativeArray which will recive two parameters: string and object, like this:
function createAssociativeArray(string, object) {
//...
}
The last item of string should get the object data. See an use/return example:
createAssociativeArray('key1.key2.key3', {
data1: 1,
data2: 2,
data3: 3
});
// key1: {
// key2: {
// key3: {
// data1: 1,
// data2: 2,
// data3: 3
// }
// }
// }
What's the most simple and robust method to do it?
Use eval isn't is a possibility.
What I was tried:
function createAssociativeArray(string, object) {
string = string.split('.');
return string.reduce(function(_object, _target, i) {
_object[_target] = (i + 1 === string.length ? object : {});
return _object;
}, {});
}
It didn't produced the expected result because the object is reseted to {}.
[JSFiddle]
Here's what I came up with:
function createAssociativeArray(string, object) {
var parts = string.split('.');
var last = parts[parts.length - 1];
var tree = {};
var node = parts.slice(0, -1).reduce(function (memo, current) {
return (memo[current] = {});
}, tree);
node[last] = object;
return tree;
}
I was curious to see if I could make a recursive solution, so here it is:
function createAssociativeArray(string, object) {
if (string === "") return object;
var stringarr = string.split('.');
var laststr = stringarr.pop();
var newobj = {};
newobj[laststr] = object;
return createAssociativeArray(stringarr.join("."), newobj);
}
Working JSFiddle demo: https://jsfiddle.net/pt352dxg/
Possible implementation:
Working demo
function createChain(keys, value) {
var obj = {};
var target = obj;
keys = keys.split('.');
keys.forEach(function(key, index) {
target = target[key] = index === keys.length - 1 ? value : {};
});
target = value;
return obj;
}
This function actually can accept an optional existing Object ({k:2, kk: 3, key1: 4}) and merge that with given json path. e.g. Try on chrome debugger console:
JSON.stringify(createAssociativeArray('key1.key2.key3', { data1: 1, data2: 2, data3: 3}, {k:2,kk:3, key1:{}}))
will print this:
"{"k":2,"kk":3,"key1":{"key2":{"key3":{"data1":1,"data2":2,"data3":3}}}}"
..
function createAssociativeArray(key, value, data) {
if(!finalData && data)
finalData = data;
var finalData;
if (!data)
data = finalData = {};
var keys = key.split('.');
if (keys.length < 2) {
data[keys[0]] = value;
} else {
if (!data[keys[0]])
data[keys[0]] = {};
data = data[keys.shift()];
createAssociativeArray(keys.join("."),value,data);
}
return finalData;
};
You were pretty close in your original attempt.
function createAssociativeArray(string, object) {
return string.split('.').reverse().reduce(function (inner, key) {
var outer = {};
outer[key] = inner;
return outer;
}, object);
}
http://jsfiddle.net/xewoa06t/
This worked for me:
function createAssociativeArray(string, object){
var array = string.split('.');
var aArray = {};
if(array.length > 1){
aArray[array[array.length - 1]] = object;
array.splice(array.length - 1, 1);
createAssociativeArray(array.join('.'), aArray)
}else{
aArray[array[array.length - 1]] = object;
return aArray
}
};
createAssociativeArray('key1.key2.key3', {data1: 1, data2: 2, data3: 3});
Basically, builds object from ground up, starting with the original object, then wrapping the 'layers' around it recursively
Nice case for a recursive function!
function createAssociativeArray(string, object) {
if (string.split('.').length == 1) {
var outObj = {};
outObj[string] = object;
return outObj;
} else {
var outObj = {};
outObj[string.split('.')[0]] = createAssociativeArray(string.split('.').slice(1).join('.'), object);
return outObj;
}
}
It's easier with a simple loop, the key point is doing in reverse (like #JustcallmeDrago)
function createAssociativeArray(keys, data)
{
var temp, keyPart
for(keys = keys.split('.'); keys.length; data = temp)
{
keyPart = keys.pop()
temp = {}
temp[keyPart] = data
}
return data
}
// TEST
x = createAssociativeArray("key1.key2.key3", { data1: "value1", data2: "value2" })
document.write('<pre>'+x+'\n'+x.key1 +'\n'
+x.key1.key2 + '\n'
+x.key1.key2.key3 +'\n'
+x.key1.key2.key3.data1 +'\n'
+x.key1.key2.key3.data2 +'</pre>')
Since no one have proviced a while-loop solution:
function namespace(path, context) {
var obj = context;
var s = path.split('.');
var p;
while (s.length) {
p = s.shift();
obj = obj[p] || (obj[p] = {});
}
return context;
}
ES6 one liner
(str, obj) => str.split('.').reverse().reduce((inner, key) => ({[key]: inner}), obj);

How to convert square bracket object keys from URL location into nested object in Javascript?

With:
var obj = { "object[foo][bar][ya]": 100 };
How can I create:
var obj = { object: { foo: { bar: { ya: 100 }}}};
Manual approach
Split the given string with bracket, then iterate through the resultant tokens to make the nested object:
Given
var obj = { "object[foo][bar][ya]": 100 };
Split them so we get
var tokens = Object.keys(obj)[0]
.split('[')
.map(function(s){return s.replace(']','')});
// tokens = [ 'object', 'foo', 'bar', 'ya' ]
Then make the nested object, inside out
var result = {};
tokens.reverse().forEach(function(key){
if (Object.keys(result).length==0){
result[key] = obj[Object.keys(obj)[0]]; // inner-most key-value
}
else{
var temp = {};
temp[key] = result;
result = temp;
}
});
Result
{"object":{"foo":{"bar":{"ya":100}}}}
Their is no native things in javascript fr parsing nested object in querystring.
You can use http://medialize.github.io/URI.js/ which is pretty damn good at the job.
console.log(URI.parseQuery("?&foo=bar&&foo=bar&foo=baz&"));
If you don't want to import the full library, this is just the part for querystring parsing (full credit to https://github.com/medialize/URI.js):
var URI = {
decodeQuery: function(string, escapeQuerySpace) {
string += '';
try {
return decodeURIComponent(escapeQuerySpace ? string.replace(/\+/g, '%20') : string);
} catch(e) {
// we're not going to mess with weird encodings,
// give up and return the undecoded original string
// see https://github.com/medialize/URI.js/issues/87
// see https://github.com/medialize/URI.js/issues/92
return string;
}
},
parseQuery: function(string, escapeQuerySpace) {
if (!string) {
return {};
}
// throw out the funky business - "?"[name"="value"&"]+
string = string.replace(/&+/g, '&').replace(/^\?*&*|&+$/g, '');
if (!string) {
return {};
}
var items = {};
var splits = string.split('&');
var length = splits.length;
var v, name, value;
for (var i = 0; i < length; i++) {
v = splits[i].split('=');
name = URI.decodeQuery(v.shift(), escapeQuerySpace);
// no "=" is null according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#collect-url-parameters
value = v.length ? URI.decodeQuery(v.join('='), escapeQuerySpace) : null;
if (Object.prototype.hasOwnProperty.call(items, name)) {
if (typeof items[name] === 'string') {
items[name] = [items[name]];
}
items[name].push(value);
} else {
items[name] = value;
}
}
return items;
}
};
You could get the parts and build a new object.
const obj = {
"object[foo][bar][ya]": 100,
"object[foo][baz]": 200,
"object[foo][bar][bar]": 50,
"xy": 30
};
let newObj = {};
for (const i in obj) {
let a = i.match(/([^\[\]]+)(\[[^\[\]]+[^\]])*?/g),
p = obj[i];
j = a.length;
while (j--) {
q = {};
q[a[j]] = p;
p = q;
}
// merge object
let k = Object.keys(p)[0],
o = newObj;
while (k in o) {
p = p[k];
o = o[k];
k = Object.keys(p)[0];
}
o[k] = p[k];
}
console.log(newObj);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here's an es6 version. Caution: Hasn't been tested for edge cases.
const keyPattern = /^(\w+)\[(\w+)\](.*)$/;
export function decodeParams(params) {
return Object.keys(params).reduce((result, key) => {
let match = key.match(keyPattern);
if (match && match.length >= 3) {
let [key, nextKey, rest = ''] = match.slice(1);
result[key] = Object.assign(
{},
result[key],
decodeParams({ [nextKey + rest]: params[key] })
);
} else {
result[key] = params[key];
}
return result;
}, {});
}

Javascript Recursion for creating a JSON object

needing some advice on how to do this properly recursively.
Basically what I'm doing, is entering in a bunch of text and it returns it as JSON.
For example:
The text:
q
b
name:rawr
Returns:
[
"q",
"b",
{
"name": "rawr"
}
]
And the following input:
q
b
name:rawr:awesome
Would return (output format is not important):
[
"q",
"b",
{
"name": {
"rawr": "awesome"
}
}
]
How can I modify the following code to allow a recursive way to have objects in objects.
var jsonify = function(input){
var listItems = input, myArray = [], end = [], i, item;
var items = listItems.split('\r\n');
// Loop through all the items
for(i = 0; i < items.length; i++){
item = items[i].split(':');
// If there is a value, then split it to create an object
if(item[1] !== undefined){
var obj = {};
obj[item[0]] = item[1];
end.push(obj);
}
else{
end.push(item[0]);
}
}
// return the results
return end;
};
I don't think recursion is the right approach here, a loop could do that as well:
var itemparts = items[i].split(':');
var value = itemparts.pop();
while (itemparts.length) {
var obj = {};
obj[itemparts.pop()] = value;
value = obj;
}
end.push(value);
Of course, as recursion and loops have equivalent might, you can do the same with a recursive function:
function recurse(parts) {
if (parts.length == 1)
return parts[0];
// else
var obj = {};
obj[parts.shift()] = recurse(parts);
return obj;
}
end.push(recurse(items[i].split(':')));
Here is a solution with recursion:
var data = [];
function createJSON(input) {
var rows = input.split("\n");
for(var i = 0; i < rows.length; i++) {
data.push(createObject(rows[i].split(":")));
}
}
function createObject(array) {
if(array.length === 1) {
return array[0];
} else {
var obj = {};
obj[array[0]] = createObject(array.splice(1));
return obj;
}
}
createJSON("p\nq\nname:rawr:awesome");
console.log(data);

Categories

Resources