how to unwind javascript object into Json object without duplicate entries - javascript

existingJsObj = {"a": [1,2,3,4], "b":[11,22,33,44]}
I want to convert this javascript into something that doesnt have array items in it, like below
desiredJsObj = [{"a":1, "b":11},{"a":2,"b":22},{"a":3, "b":33},{"a":4, "b":44}]

You could iterate the keys and the values while using a new array and object for the result.
var object = { a: [1, 2, 3, 4], b: [11, 22, 33, 44] },
array = Object.keys(object).reduce(function (r, k) {
object[k].forEach(function (v, i) {
(r[i] = r[i] || {})[k] = v;
});
return r;
}, []);
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }

I would do following:
const income = {"a": [1,2,3,4], "b":[11,22,33,44]};
const res = income.a.map((a, i) => ({ a: a, b: income.b[i] }));
This works in assumption that "a" length is equal to "b" length.

try this
existingJsObj = {"a": [1,2,3,4], "b":[11,22,33,44]}
desiredJsObj = []
keys = Object.keys(existingJsObj);
existingJsObj[keys[0]].forEach((val, index) => {
value = {};
keys.forEach((key, i) => value[key] = existingJsObj[key][index])
desiredJsObj.push(value)
})

Related

Iterative depth-first traversal with remembering value's paths

I need help with specific implementation of iterative depth first traversal algorithm.
I have an object like this (it's just an example, object might have more properties and be deeper nested):
const root = {
a: 1,
b: {
c: {
d: {
e: 2,
f: 3,
}
},
g: [
{
h: 4,
i: 5,
},
{
j: 6,
k: 7,
}
]
}
}
What I need is a function that would traverse the whole object and return an array like this:
[
{"a": 1},
{"b.c.d.e": 2},
{"b.c.d.f": 3},
{"b.g.0.h": 4},
{"b.g.0.i": 5},
{"b.g.1.j": 6},
{"b.g.1.k": 7},
]
I managed to create an algorithm that sort of solves my problem, but needs one additional step in the end. Result of the algorithm is an array of strings like that:
[
'a^1',
'b.c.d.e^2',
'b.c.d.f^3',
'b.g.0.h^4',
'b.g.0.i^5',
'b.g.1.j^6',
'b.g.1.k^7'
]
so in order to achieve what I want I have to do one full iteration over the result of my algorithm, split strings by ^ symbol and then create objects based on that.
This is the part that I need help with - how can I improve/change my solution so I don't need to do that last step?
function dft(root) {
let stack = [];
let result = [];
const isObject = value => typeof value === "object";
stack.push(root);
while (stack.length > 0) {
let node = stack.pop();
if (isObject(node)) {
Object.entries(node).forEach(([childNodeKey, childNodeValue]) => {
if (isObject(childNodeValue)) {
const newObject = Object.fromEntries(
Object.entries(childNodeValue).map(([cnk, cnv]) => {
return [`${childNodeKey}.${cnk}`, cnv];
})
);
stack.push(newObject);
} else {
stack.push(`${childNodeKey}^${childNodeValue}`);
}
})
} else {
result.push(node);
}
}
return result.reverse();
}
I'd keep pairs <keys,value> in the stack and only create a string key when storing a newly created object:
function dft(obj) {
let stack = []
let res = []
stack.push([[], obj])
while (stack.length) {
let [keys, val] = stack.pop()
if (!val || typeof val !== 'object') {
res.push({
[keys.join('.')]: val
})
} else {
Object.entries(val).forEach(p => stack.push([
keys.concat(p[0]),
p[1],
]))
}
}
return res.reverse()
}
//
const root = {
a: 1,
b: {
c: {
d: {
e: 2,
f: 3,
}
},
g: [
{
h: 4,
i: 5,
},
{
j: 6,
k: 7,
}
]
}
}
console.log(dft(root))
You can push the childNodeKey childNodeValue pair directly as an object to your result array.
Change
stack.push(`${childNodeKey}^${childNodeValue}`);
to
const newEntry = {}
newEntry[childNodeKey] = childNodeValue
result.push(newEntry);
or with ES2015 syntax (you would need a transpiler for browser compatibility)
result.push({[childNodeKey]: childNodeValue});
Complete function:
const root = {
a: 1,
b: {
c: {
d: {
e: 2,
f: 3,
}
},
g: [
{
h: 4,
i: 5,
},
{
j: 6,
k: 7,
}
]
}
}
function dft(root) {
let stack = [];
let result = [];
const isObject = value => typeof value === "object";
stack.push(root);
while (stack.length > 0) {
let node = stack.pop();
if (isObject(node)) {
Object.entries(node).forEach(([childNodeKey, childNodeValue]) => {
if (isObject(childNodeValue)) {
const newObject = Object.fromEntries(
Object.entries(childNodeValue).map(([cnk, cnv]) => {
return [`${childNodeKey}.${cnk}`, cnv];
})
);
stack.unshift(newObject);
} else {
const newEntry = {}
newEntry[childNodeKey] = childNodeValue
result.push({[childNodeKey]: childNodeValue});
}
})
} else {
result.push(node);
}
}
return result;
}
console.log(dft(root))
As you mentioned, you almost got it complete. Just make the array entry an object just before pushing it into result. By splitting Array.prototype.split('^') you can get 'b.g.0.h^4' >>> ['b.g.0.h', '4']. So, rest is a cake:
if (isObject(node)) {
...
} else {
const keyAndValue = node.split('^')
// approach 1)
// const key = keyAndValue[0]
// const value = keyAndValue[1]
// dynamic key setting
// result.push({[key]: value});
// approach 2)
// or in short,
// dynamic key setting
result.push({[keyAndValue[0]]: keyAndValue[1]});
}
You could use a stack where each item has an iterator over the children, and the path up to that point:
function collect(root) {
const Node = (root, path) =>
({ iter: Object.entries(root)[Symbol.iterator](), path });
const result = [];
const stack = [Node(root, "")];
while (stack.length) {
const node = stack.pop();
const {value} = node.iter.next();
if (!value) continue;
stack.push(node);
const [key, child] = value;
const path = node.path ? node.path + "." + key : key;
if (Object(child) !== child) result.push({ [path]: child });
else stack.push(Node(child, path));
}
return result;
}
const root = {a:1,b:{c:{d:{e:2,f:3}},g:[{h:4,i:5},{j:6,k:7}]}};
console.log(collect(root));
I would suggest that the quickest fix to your code is simply to replace
return result.reverse();
with
return result.reverse()
.map ((s, _, __, [k, v] = s .split ('^')) => ({[k]: v}));
But I also think that we can write code to do this more simply. A function I use often will convert your input into something like this:
[
[["a"], 1],
[["b", "c", "d", "e"], 2],
[["b", "c", "d", "f"], 3],
[["b", "g", 0, "h"], 4],
[["b", "g", 0, "i"], 5],
[["b", "g", 1, "j"], 6],
[["b", "g", 1, "k"], 7]
]
and a fairly trivial wrapper can then convert this to your output. It could look like this:
const pathEntries = (obj) =>
Object (obj) === obj
? Object .entries (obj) .flatMap (
([k, x]) => pathEntries (x) .map (([p, v]) => [[Array.isArray(obj) ? Number(k) : k, ... p], v])
)
: [[[], obj]]
const transform = (o) =>
pathEntries (o)
.map (([k, v]) => ({[k .join ('.')] : v}))
const root = {a: 1, b: {c: {d: {e: 2, f: 3, }}, g: [{h: 4, i: 5, }, {j: 6, k: 7}]}}
console .log (transform (root))
.as-console-wrapper {max-height: 100% !important; top: 0}
I don't know your usecase, but I would find this output generally more helpful:
{
"a": 1,
"b.c.d.e": 2,
"b.c.d.f": 3,
"b.g.0.h": 4,
"b.g.0.i": 5,
"b.g.1.j": 6,
"b.g.1.k": 7
}
(that is, one object with a number of properties, rather than an array of single-property objects.)
And we could do this nearly as easily, with a small change to transform:
const transform = (o) =>
pathEntries (o)
.reduce ((a, [k, v]) => ((a[k .join ('.')] = v), a), {})

Convert array to map with predefined keys

I have an data in an array that looks like that:
let data = {[val1, val2, val3], [val4, val5, val6]}
I need to convert it to a map with predefined keys:
let keys = [key1, key2, key3]
I would like my output to be key-value map like this:
0: {key1:value1, key2:value2, key3:value3}
1: {key1:value4, key2:value5, key3:value6}
I tried:
let obj = Object.assign(keys, data)
But output is:
0: (3) [value1, value2, value3]
1: (3) [value4, value5, value6]
Use Object.fromEntries and map
const data = [
[1, 2, 3],
[4, 5, 6]
];
const keys = ["key1", "key2", "key3"];
const updated = data.map(arr =>
Object.fromEntries(arr.map((item, i) => [keys[i], item]))
);
console.log(updated);
You can make use of .map() and .reduce() functions to get the desired output:
let data = [['val1', 'val2', 'val3'], ['val4', 'val5', 'val6']];
let keys = ['key1', 'key2', 'key3'];
let result = data.map(
vals => vals.reduce((r, c, i) => (r[keys[i]] = vals[i], r), {})
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Simple solution.
let data = [
[1, 2, 3],
[4, 5, 6]
];
let keys = ["key1", "key2", "key3"];
const res = data.map(([v1, v2, v3]) => {
return {
[keys[0]]: v1,
[keys[1]]: v2,
[keys[2]]: v3
};
});
console.log(res);
// Also
const res2 = data.map(arr => {
let map = {};
keys.forEach((key, index) => {
map[key] = arr[index];
});
return map;
});
console.log(res2);
// Also
const res3 = data.map(arr =>
keys.reduce((o, key, index) => {
o[key] = arr[index];
return o;
}, {})
);
console.log(res3);
.as-console-wrapper { max-height: 100% !important; top: 0; color: blue; background: #fff}

Rearrange dynamic Object to form a structured Object

I have an Object like this:
const val = {"abc":{"1":1, "2":6,"3":5},"def":{"1":3, "2":4,"3":8},"xyz":{"1":5, "2":6,"3":7}}
I want to transform the object data like below:
[{"abc":1,"def":3,"xyz":5},{"abc":6,"def":4,"xyz":6}, ...]
All the values are dynamic, any number of inner object may be there
I have tried like this:
const val = {"abc":{"1":1, "2":6,"3":5},"def":{"1":3, "2":4,"3":8},"xyz":{"1":5, "2":6,"3":7}}
let dataObj = {};
let secondArr = [];
let dataArr =[]
Object.entries(val).map(firstObj=>{
Object.entries(firstObj[1]).forEach(secondObj => {
dataObj={[firstObj[0]]:secondObj[1]};
secondArr.push(dataObj);
})
dataArr.push(secondArr)
})
console.log(dataArr)
Can anyone tell me a solution for this?
Thanks in advance
You could iterate the entries of the objects and take the inner keys as indices of the array with new objects with outer key and value.
var data = { abc: { 1: 1, 2: 6, 3: 5 }, def: { 1: 3, 2: 4, 3: 8 }, xyz: { 1: 5, 2: 6, 3: 7 } },
result = Object
.entries(data)
.reduce((r, [k, o]) => {
Object.entries(o).forEach(([i, v]) =>
Object.assign(r[i - 1] = r[i - 1] || {}, { [k]: v }));
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Mapping key and value to new keys in Javascript

Array of dictionaries should be converted simpler form.
data = [{A:1},{B:2},{C:3}]
data = {A: 1, B: 2}
data = ["0":{ A : 1, B : 2 , C : 3}]
Both are completely different datasets. I'm trying to map it also like below format.
The above should become like
data = [
{
name: "A",
y: 1
},
{
name: "B",
y: 2
},
{
name: "C",
y: 3
}
];
I tried this following approach but it's wrong
name = {}
data.forEach(function(k,x){
return name['name'] = k , name["y"] = x
})
Please suggest me a better approach.
map each object's entries to extract the key and the value, and return an object with name and y keys:
const data = [{A:1},{B:2},{C:3}]
const output = data.map(item => {
const [name, y] = Object.entries(item)[0];
return { name, y };
});
console.log(output);
If the keys (A, B, etc) are guaranteed to be unique throughout the array, then everything becomes simpler.
const data = [{A:1},{B:2},{C:3}];
const merged = Object.assign({}, ...data);
const newData = Object.entries(merged)
.map(([name, y]) => ({ name, y }));
console.log(newData);
However, if the keys aren't guaranteed unique, then refer to CertainPerformance's answer.
you can implement like this
var data = [{A:1},{B:2},{C:3}];
var reformattedArra = data.map(obj => {
let val = {};
val.name = Object.keys(obj)[0];
val.y = obj[Object.keys(obj)[0]];
return val;
})
console.log(JSON.stringify(reformattedArra));
I would say, use Object.keys() which is widly supported
let data = [{A:1},{B:2},{C:3}];
data = Object.assign({}, ...data);
data = Object.keys(data).map(key => ({ name: key, y: data[key] }));
console.log(data);
You yould could chekc the data format and if it is not an array, build one and reduce the array by taking the objetcs and create for each key/value a new object for the result set.
function simple(data) {
return (Array.isArray(data) ? data : [data]).reduce((r, o) => [...r, ...Object.entries(o).map(([name, y]) => ({ name, y }))], []);
}
console.log(simple([{ A: 1 }, { B: 2 }, { C: 3, D: 4 }]));
console.log(simple({ A: 1, B: 2 }));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Multidimensional array comparison in Javascript

My input is
let data = [
[1,2,3],
[1,3,2,4],
[3,2,1,5],
[1,2,3],
[3,2,1]
];
after this peace of code:
var dataUnique = data.reduce(function (out, item) {
return out.concat(out.filter(function (comp) {
return item.toString() == comp.toString();
}).length ? [] : [item])
}, []);
console.log(data, dataUnique);
Output give me array of 4 element
[1,2,3],
[1,3,2,4],
[3,2,1,5],
[3,2,1]
but expected output would be
[1,2,3],
[1,3,2,4],
[3,2,1,5]
Can anyone suggest any solution.
Thanks.
You can create some sort of hash — on object, Map, Set, etc and use a stringified version of your input as keys. Here's an example using a Set:
let data = [
[1,2,3],
[1,3,2,4],
[3,2,1,5],
[1,2,3],
[3,2,1]
];
let set = new Set()
let result = data.reduce((a, i) => {
let k = i.concat().sort().join('_')
if (!set.has(k)) {
set.add(k)
a.push(i)
}
return a
}, [])
console.log(result)
This could be a little simpler if you didn't mind the output having sorted versions of your input.
This is an alternative using the functions reduce, every and includes.
Basically, this approach checks if one number doesn't exist within the previously checked arrays.
let data = [ [1, 2, 3], [1, 3, 2, 4], [3, 2, 1, 5], [1, 2, 3], [3, 2, 1]],
result = data.reduce((a, c) => {
c.forEach(n => {
if (a.length == 0 || !a.every(arr => arr.includes(n))) a.push(c);
});
return a;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories

Resources