I have some data that looks like this
let arr = [
{
a:1,
b:2,
c:3
},
{
a:4,
b:5,
c:6
},
{
a:7,
b:8,
c:9
}
]
and I'd like to get it to reformat like this
{
a: [1,4,7],
b: [2,5,8],
c: [3,6,9]
}
Here is my solution:
let arr = [
{
a:1,
b:2,
c:3
},
{
a:4,
b:5,
c:6
},
{
a:7,
b:8,
c:9
}
]
// {
// a: [1,4,7],
// b: [2,5,8],
// c: [3,6,9]
// }
function practice (arr) {
console.log(typeof arr) // WHY IS THIS RETURNING AN OBJECT???
let keys = Object.keys(arr[0])
const resultObj = {}
for (let key of keys) {
resultObj[key] = []
}
arr.forEach((x,idx)=> {
for (let key in x) {
resultObj[key].push(x[key])
}
})
return resultObj
}
practice(arr)
I know that my solution is not the most efficient method. While I completed the exercise, I am having trouble understanding the concepts below:
At first glance, arr to me seems like an array with a single index
containing three objects. For example, arr[0] = {obj1},{obj2},{obj3}, but
I performed a typeof check on arr and it returned object.
When I console log arr at a specified index arr[1], it prints out {a:4,b:5,c:6} as if it is an array.
My question is what is happening here and what exactly is this type of data structure?
Please offer me a more clean and efficient code to this question and explain to me the concepts.
Try
function practice (arr) {
let resultObj = {};
arr.forEach((x) => {
for (let key in x) {
if (resultObj.hasOwnProperty(key)) {
resultObj[key].push(x[key]);
} else {
resultObj[key] = [x[key]];
}
}
});
return resultObj;
}
In order to check for an array, you should make use of Array.isArray() method. typeof will give you an object since Array is essentially a form of object in javascript created using the Object constructor.
To get a desired output, all you need to do is to loop over the array and store the values in an object
let arr = [
{
a:1,
b:2,
c:3
},
{
a:4,
b:5,
c:6
},
{
a:7,
b:8,
c:9
}
]
var res = {};
arr.forEach((obj) => {
Object.entries(obj).forEach(([key, val]) => {
if(res[key]) {
res[key].push(val);
} else {
res[key] = [val];
}
})
});
console.log(res);
You may use array reduce() method for this like:
Loop through all the keys of each object in the array using Object.keys(o) inside the reduce() method
Inside the loop, initialize the accumulator with the same key as we have inside the loop and the initial value of that key as empty array [].
Then using r[k].push(o[k]), we are adding the matching key values inside this array.
Then finally return the object r from the .reduce() method.
let arr = [{a:1,b:2,c:3},{a:4,b:5,c:6},{a:7,b:8,c:9}];
const res = arr.reduce((r, o) => {
Object.keys(o).forEach((k) => {
r[k] = r[k] || [];
r[k].push(o[k])
});
return r;
}, {})
console.log(res)
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can try this-
let arr = [
{
a:1,
b:2,
c:3
},
{
a:4,
b:5,
c:6
},
{
a:7,
b:8,
c:9
}
];
let res = {};
Object.values(arr).forEach(value => {
Object.keys(value).forEach(key => {
if (typeof res[key] === 'undefined') {
res[key] = [];
}
res[key].push(value[key])
})
})
console.log(res);
With multiple forEach loops build an Object with the keys and value are merged for same key.
let arr = [
{
a: 1,
b: 2,
c: 3
},
{
a: 4,
b: 5,
c: 6
},
{
a: 7,
b: 8,
c: 9
}
];
const res = {};
arr.forEach(item => {
Object.keys(item).forEach(
key => (res[key] = key in res ? [...res[key], item[key]] : [item[key]])
);
});
console.log(res);
Related
I would like to merge an array with another array. The only catch is that each array is within an object.
Intuitively I tried {...arrObj, ...newArrObj} however this leads newArrObj overwriting items in the arrObj.
const array = ['an', 'array'];
const newArray = [, , 'new', 'ehrray'];
const obj = {
key: { ...array
}
};
const newObj = {
key: { ...newArray
}
};
const merged = { ...obj,
...newObj
};
console.log(merged);
I would expect merged to be:
{
"key": {
"0": "an",
"1": "array",
"2": "new",
"3": "ehrray"
}
}
but receive
{
"key": {
"2": "new",
"3": "ehrray"
}
}
This might be useful
const a0 = ['1', '2', undefined , undefined, '5', '6', '7'];
const a1 = [undefined, undefined, '3', '4'];
function merge(a, b) {
return a.map(function(v,i){ return v?v:b[i]});
}
console.log(a0 > a1?merge(a0, a1):merge(a1, a0));
I wanted to updated that I ended up going with a recursive merge to get the nested object containing an array merged.
const array = ['an', 'array'];
const newArray = [, , 'new', 'ehrray'];
const obj = {
key: { ...array
}
};
const newObj = {
key: { ...newArray
}
};
const merge = (obj1, obj2) => {
const recursiveMerge = (obj, entries) => {
for (const [key, value] of entries) {
if (typeof value === "object") {
obj[key] = obj[key] ? { ...obj[key]
} : {};
recursiveMerge(obj[key], Object.entries(value))
} else {
obj[key] = value;
}
}
return obj;
}
return recursiveMerge(obj1, Object.entries(obj2))
}
console.log(merge(obj, newObj));
The idea is that there are unset values with only a few set. eg. const newArray = new Array(4); newArray[2] = 'new';
{ value: null }, even { value: undefined } is not the same thing as { foo: 42 } with no value at all. That's the reason that in your example "an" and "array" are overwritten with the nulls from the newArray.
This particular example you can solve by swapping the order in which you add the arrays to the result, but as soon as both arrays contain null-values there is no way to do it with spread-syntax / Object.assign alone. You have to implement the behaviour:
const array = new Array('an', 'array', null, null, "and", "more", "from", "array");
const newArray = new Array(null, null, 'new', 'ehrray');
function merge(a, b) {
const result = [];
for (let i = 0; i < a.length || i < b.length; ++i) {
result[i] = b[i] == null ? a[i] : b[i];
}
return result;
}
console.log(merge(array, newArray));
Imagine I have this array of arrays:
myData = [
["wantThisAsKey1",someElement1,anotherElement1],
["wantThisAsKey2",someElement2,anotherElement2]
]
I need to convert this array to an object where the first element of each array is used as the key:
myDataObject = {
"wantThisAsKey1": [someElement1,anotherElement1],
"wantThisAsKey2": [someElement2,anotherElement2],
}
How can I do this in a general way, something like myDataObject = convertToObject(myData) ?
Try this:
let myData = [
["wantThisAsKey1","someElement1","anotherElement1"],
["wantThisAsKey2","someElement2","anotherElement2"]
];
let myDataObject = convertToObject(myData);
console.log(myDataObject);
function convertToObject(data){
let res = {};
for(let i = 0; i < data.length; i++)
res[data[i][0]] = data[i].slice(1);
return res;
}
To achieve this you can combine the arrays reduce function with destructuring assignment:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
myData = [
["wantThisAsKey1", 1, 2],
["wantThisAsKey2", 2, 3]
]
const newobject = myData.reduce((acc, elem) => {
const [key, ...rest] = elem;
acc[key] = rest
return acc;
}, {})
console.log(newobject);
// Result: { wantThisAsKey1: [ 1, 2 ], wantThisAsKey2: [ 2, 3 ] }
try using reduce here. like this.
const result = myData.reduce( (result, ele) => {
const [key, ...other] = ele
result[key] = other
return result
}, {})
// output = { wantThisAsKey1: [ 'someElement1', 'anotherElement1' ], wantThisAsKey2: [ 'someElement2', 'anotherElement2' ] }
I'm searching a for a way to create a function. in which I can pass an object and an array of properties (keys) I want gone. That function will return me a new object that doesn't have the keys I've specified.
function(keys: array, obj: object) {...}
Question is - how do I do that with multiple properties?
I've searched and only found this kind of solution:
const myObject = {
a: 1,
b: 2,
c: 3
};
const { a, ...noA } = myObject;
But it only works if I want to remove only ONE key. What if I want to remove multiple, using an array I just passed? How do I do that without mutating the original array or manually creating copies of it?
You could destructure the object by taking a computed property for unwanted properties.
const
without = (object, keys) => keys.reduce((o, k) => {
const { [k]: _ , ...p } = o;
return p;
}, object),
myObject = { a: 1, b: 2, c: 3 },
keys = ['a', 'b'],
result = without(myObject, keys);
console.log(result);
You can do it using reduce and Object.entries(). You can try this:
const myObject = {
a: 1,
b: 2,
c: 3,
d: 4
};
const removeProps = (object, keys) => {
return Object.entries(object).reduce((a, [key, value]) => (keys.indexOf(key) === -1 ? {...a, [key]: value}: a), {});
}
console.log(removeProps(myObject, ['b']));
console.log(removeProps(myObject, ['b', 'c']));
console.log('Original Object: ', myObject);
.as-console-wrapper{min-height: 100%!important; top: 0}
Above answers are great, I'm sharing my try:
var myObject = { a: 1, b: 2, c: 3, d: 4};
let remove=(obj, arr)=> {
let output=[];
for(const [key, value] of Object.entries(obj)){
if(!arr.includes(key)){
output.push([key,value]);
}
}
return Object.fromEntries(output);
}
console.log(remove(myObject, ['a']));
console.log(remove(myObject, ['a', 'c']));
var obj = {
a: {
aa: {
aaa: {
aaaa: "a"
}
}
},
b: {
bb: {
bbb: "b"
}
}
}
flatten(obj)
//=>[["a","b"],["aa","bb"],["aaa","bbb"],["aaaa"]]
This is interesting question,my friend says BFS or DFS can be able to solve the problem,but I can't
you can use recursion and keep a level counter, on each step of recursion add keys to the result array. For this you have to check if an array already exists at that level then concatenate to that array.
var obj = {
a: {
aa: {
aaa: {
aaaa: "a"
}
}
},
b: {
bb: {
bbb: "b"
}
}
}
var result = [];
function flatten(obj, level){
var keys = Object.keys(obj);
result[level] = result[level] !== undefined ? result[level].concat(keys) : keys;
keys.forEach(x => typeof obj[x] === 'object' && flatten(obj[x], level + 1));
}
flatten(obj, 0);
console.log(result);
I have a function used to flatten objects like so:
let object = {
a: 1,
b: [
{ c: 2 },
{ c: 3 }
]
};
flatten(object)
// returns {
'a': 1,
'b.0.c': 2,
'b.1.c': 3
}
I need to unflatten objects, but also revert arrays to how they were. I have the following code:
unflatten(obj) {
let final = {};
for (let prop in obj) {
this.assign(final, prop.split('.'), obj[prop]);
}
return final;
}
assign(final, path, value) {
let lastKeyIndex = path.length-1;
for (var i = 0; i < lastKeyIndex; ++ i) {
let key = path[i];
if (!(key in final)) {
final[key] = {};
}
final = final[key];
}
final[path[lastKeyIndex]] = value;
}
which works for the most part, but it treats arrays like so:
{
a: 1,
b: { // Notice how b's value is now an object
"0": { c: 2 }, // Notice how these now have a key corresponding to their index
"1": { c: 3 }
}
}
Whereas I need b to be an array like before:
{
a: 1,
b: [
{ c: 2 },
{ c: 3 }
]
}
I'm at a loss for where to go from here. It needs to be able to deal with an arbitrary number of arrays like:
'a.b.0.c.0.d',
'a.b.0.c.1.d',
'a.b.1.c.0.d',
'a.b.1.c.1.d',
'a.b.1.c.2.d',
// etc
It needs to be vanilla JS, but es2015 is fine. It it assumed that any key that's a number is actually part of an array.
If anyone has any advice, it's appreciated!
When you find that key is not in final, you should check to see if the next key in the path is only digits (using a regular expression) and, if so, assign to an array instead of an object:
if (!(key in final)) {
final[key] = /^\d+$/.test(path[i + 1]) ? [] : {};
}
let object = {
a: 1,
b: [{
c: 2
},
{
c: 3
}
]
};
let flattened = {
'a': 1,
'b.0.c': 2,
'b.1.c': 3
}
function unflatten(obj) {
let final = {};
for (let prop in obj) {
assign(final, prop.split('.'), obj[prop]);
}
return final;
}
function assign (final, path, value) {
let lastKeyIndex = path.length - 1;
for (var i = 0; i < lastKeyIndex; ++i) {
let key = path[i];
if (!(key in final)) {
final[key] = /^\d+$/.test(path[i + 1]) ? [] : {};
}
final = final[key];
}
final[path[lastKeyIndex]] = value;
}
console.log(unflatten(flattened))
.as-console-wrapper { min-height: 100vh; }
You could iterate the keys and then split the string for single properties. For building a new object, you could check for number and take an array for these properties.
function setValue(object, path, value) {
var way = path.split('.'),
last = way.pop();
way.reduce(function (o, k, i, kk) {
return o[k] = o[k] || (isFinite(i + 1 in kk ? kk[i + 1] : last) ? [] : {});
}, object)[last] = value;
}
function unFlatten(object) {
var keys = Object.keys(object),
result = isFinite(keys[0][0]) ? [] : {};
keys.forEach(function (k) {
setValue(result, k, object[k]);
});
return result;
}
console.log(unFlatten({
'a': 1,
'b.0.c': 2,
'b.1.c': 3
}));
console.log(unFlatten({
'0': 1,
'1.0.c': 2,
'1.1.c': 3
}));
.as-console-wrapper { max-height: 100% !important; top: 0; }