Convert associative array to standard array in Javascript - javascript

Which is the best way to "convert" associative array to standard (0 based indexes) array. Is there any other way than iteration and rewriting each item?
I have an array which was created by counting appearances of some properties and it is needed that way. The output array lookes like:
let ASSARR = {
"abc": { "c": 5, "p": 3 },
"def": { "c": 1, "p": 10 },
"ghi": { "c": 15, "p": 7 }
};
...and so.
I need to filter and sort it though then I need to "convert" it to standard array so it looked more like this:
let STARR = [
{ "i": "abc", "c": 5, "p": 3 },
{ "i": "def", "c": 1, "p": 10 },
{ "i": "ghi", "c": 15, "p": 7 }
];
Do I need to iterate by for or similar loop or maybe there is more effective way to do this?

Is there any other way than iteration and rewriting each item?
No, you'll need a loop (either in your code, or in code in the standard library that you call such as looping through the result of Object.entries).
Since it's a loop either way, I'd probably just write the loop (especially as doing so, you can loop once rather than multiple times). Here I'm assuming you want to create new objects rather than just adding an i property to the objects you have (but keep reading):
const result = [];
for (const i in ASSARR) {
result.push({
i,
...ASSARR[i],
});
}
let ASSARR = {
"abc": { "c": 5, "p": 3 },
"def": { "c": 1, "p": 10 },
"ghi": { "c": 15, "p": 7 }
};
const result = [];
for (const i in ASSARR) {
result.push({
i,
...ASSARR[i],
});
}
console.log(result);
.as-console-wrapper {
max-height: 100% !important;
}
...but if you want to modify the existing objects instead:
const result = [];
for (const i in ASSARR) {
const object = ASSARR[i];
object.i = i;
result.push(object);
}
let ASSARR = {
"abc": { "c": 5, "p": 3 },
"def": { "c": 1, "p": 10 },
"ghi": { "c": 15, "p": 7 }
};
const result = [];
for (const i in ASSARR) {
const object = ASSARR[i];
object.i = i;
result.push(object);
}
console.log(result);
.as-console-wrapper {
max-height: 100% !important;
}
Note: In the above, I'm assuming there are no inherited but enumerable properties you want left out. If there are, wrap the push calls in if (Object.hasOwn(object, i)) to skip inherited properties.

You could get the entries and map the objects with key.
const
object = { abc: { c: 5, p: 3 }, def: { c: 1, p: 10 }, ghi: { c: 15, p: 7 } },
array = Object
.entries(object)
.map(([i, o]) => ({ i, ...o }));
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }

You can just use Object.entries, and Array.map..
eg..
let ASSARR = {
"abc": { "c": 5, "p": 3 },
"def": { "c": 1, "p": 10 },
"ghi": { "c": 15, "p": 7 }
};
let STARR = Object.entries(ASSARR).map(([k,v]) => {
return {i: k, ...v};
});
console.log(STARR);

Related

I'm getting wrong output in Javascript Function?

I have function that looks through an array of objects (first argument) and returns an array of all objects that have matching name and value pairs (second argument). Each name and value pair of the source object has to be present in the object from the collection if it is to be included in the returned array.
function whatIsInAName(collection, source) {
let keyArr = Object.keys(source);
for (let i = 0; i < keyArr.length; i++) {
var arr = collection.filter(function(item) {
return item.hasOwnProperty(keyArr[i])
})
}
return arr.filter(function(item) {
for (let g = 0; g < keyArr.length; g++) {
return item[keyArr[g]] === source[keyArr[g]];
}
});
}
console.log(whatIsInAName([{
"a": 1,
"b": 2,
"c": 3
}], {
"a": 1,
"b": 9999,
"c": 3
}));
I should give an empty array [ ].
but It is giving [{"a": 1, "b": 2, "c": 3}]
You had an error in your second filter.
In for statement you were returning comparision result of first item from that statement and not checking if whole arr is equal.
In code below I have changed for statement to return false when items doesn't match. Then at the end there is returned true becouse there wasn't found any item that doesn't match with the other arr.
function whatIsInAName(collection, source) {
let keyArr = Object.keys(source);
for(let i = 0; i < keyArr.length; i++){
var arr = collection.filter(item => item.hasOwnProperty(keyArr[i]));
}
return arr.filter(item => {
for(let g = 0; g < keyArr.length; g++) {
if (item[keyArr[g]] !== source[keyArr[g]]) {
return false;
}
}
return true;
});
}
console.log(
whatIsInAName(
[{"a": 1, "b": 2, "c": 3}, {"a": 1, "b": 9999, "c": 3}],
{"a": 1, "b": 9999, "c": 3}
)
);
What is the first loop for? The arr created there is overwritten in every iteration of the loop. So the final arr will be the array of items which at least contain the last property in Object.keys(). You can apply your filter directly on the collections parameter.
function whatIsInAName(collection, source) {
let keys = Object.keys(source);
return collection.filter(item => keys.every(k => item[k] === source[k]));
}
console.log(whatIsInAName([
{"a": 1, "b": 9999, "c": 3 },
{"a": 2, "b": 9999, "c": 3 },
{"a": 1, "b": 2, "c": 3 },
{"a": 1, "b": 9999, "c": 3, "d": 4 }
],
{"a": 1, "b": 9999, "c": 3}));
Keep in mind that the equality check === will only work for primitive values but not for objects or arrays. Also this approach will accept additional properties in the items of the collection, that are not present in the source item.

Create duplicate array of object

Say I have an array of object::
const banana = [{"a":"ann","b":"bann","det":[{"c":"cat","d":"dog"},{"c":"conn","d":"donn"}]}, {"a":"auu","b":"buu","det":[{"c":"camel","d":"damel"},{"c":"coww","d":"doww"}]}]
I want to transform this array of object in this form::
const banana = [{"a":"ann","b":"bann","c":"cat","d":"dog"}, {"a":"ann","b":"bann","c":"conn","d":"donn"}, {"a":"auu","b":"buu","c":"camel","d":"damel"}, {"a":"auu","b":"buu","c":"coww","d":"doww"}]
As you can see array of object inside array of object have merged and duplicated.
I tried as:
const apple = []
for(let i = 0; i<banana.length;i++){
for(let j = 0;j<banana[i].det.length;j++{
apple.push(banana[i].det[j])
}
}
console.log(apple)
**OUTPUT: [{c: "cat", d: "dog"},{c: "conn", d: "donn"},{c: "camel", d: "damel"},{c: "coww", d: "doww"}]**
But I'm looking for the O/P as:
[{"a":"ann","b":"bann","c":"cat","d":"dog"}, {"a":"ann","b":"bann","c":"conn","d":"donn"},
{"a":"auu","b":"buu","c":"camel","d":"damel"}, {"a":"auu","b":"buu","c":"coww","d":"doww"}]
But I'm unable to form logic. I'm still trying but if i could get some guidance that would be really helpful.
**EDIT:**So I've come up with an idea using spread operator:
let enamel = {}
for(let i = 0; i<banana.length;i++){
for(let j = 0;j<banana[i].det.length;j++){
employee = {
...banana[j],
...banana[i].det[j]
};
}
}
It gives the output as:
console.log(enamel)
{a: "auu", b: "buu", det: Array(2), c: "coww", d: "doww"}
But I want to have all the objects in an array as previously stated.
You can use this logic, which copies over initial object, adds extra properties, drops the det array, and flatten the result
function extras(obj) {
// create a copy of the current context (initial obj)
// and add all properties from the extra object
obj = Object.assign({}, this, obj);
// but delete the `det` from the copy
delete obj.det;
// and return the object
return obj;
}
// per each array object ...
banana
.map(
// and per each det ...
obj => obj.det.map(extras, obj)
)
// flatten the final array of objects
.flat();
You just have to extract a and b from object in banana. I have used destructuring to extract it.
const banana = [{ "a": "ann", "b": "bann", "det": [{ "c": "cat", "d": "dog" }, { "c": "conn", "d": "donn" }] }, { "a": "auu", "b": "buu", "det": [{ "c": "camel", "d": "damel" }, { "c": "coww", "d": "doww" }] }]
const apple = []
for (let i = 0; i < banana.length; i++) {
for (let j = 0; j < banana[i].det.length; j++) {
const {a,b} = banana[i];
const {c,d} = banana[i].det[j];
apple.push({a,b,c,d});
}
}
console.log(apple)
You can do this:
const banana = [
{
"a": "ann",
"b": "bann",
"det": [{ "c": "cat", "d": "dog" }, { "c": "conn", "d": "donn" }]
},
{
"a": "auu",
"b": "buu",
"det": [
{ "c": "camel", "d": "damel" },
{ "c": "coww", "d": "doww" }
]
}
]
const result = [];
banana.forEach( b =>{
b.det.forEach(d =>{
result.push({
a: b.a,
b: b.b,
c: d.c,
d: d.d
});
});
});
console.log(result);
Try this
const banana = [{"a":"ann","b":"bann","det":[{"c":"cat","d":"dog"},{"c":"conn","d":"donn"}]}, {"a":"auu","b":"buu","det":[{"c":"camel","d":"damel"},{"c":"coww","d":"doww"}]}]
const output = []
for (const { a, b, det } of banana) {
for (const animal of det) {
output.push({a, b, ...animal })
}
}
console.log(output)
I think you want to do it like this in case you want to avoid manually take a and b and other property except 'det' properties
function getResult(banana) {
const answer = [];
banana.forEach(element => {
const arrayData = element['det'];
delete element['det'];
// remove the 'del' property temporarily
arrayData.forEach(subElement => {
answer.push({
...element, // basically spread operator to make copy of all properties each time
...subElement
});
});
// restore the 'del' proprty
element['det'] = arrayData;
});
console.log("the result is : ", answer);
return answer;
}

How to group two arrays using key in node js

I have below two objects which I want to merge.
[
{
"response_code": 1,
"response_message": [{
"a": 1000,
"b": 1000001,
"c": 10000002
}]
}]
[
{
"response_code": 1,
"response_message": [{
"a": 2000,
"b": 2000001,
"c": 20000002
}]
}
]
I want to merge them like below by having only one value of response code and merge values of response message like below way.
{
"response_code": 1,
"response_message": [
{
"a": 1000,
"b": 1000001,
"c": 10000002
},
{
"a": 2000,
"b": 2000001,
"c": 20000002
}]
}
Really stuck with such complicated merging where I want only once the value of response code and merge the values of response message.
Here I want to remove response code value from other array and merge/group the response message value with fist array.
I whipped up a little function for you:
And in accordance with the concerns raised by you in the comments, along with the test case where response_message was a string, I have edited the code snippet to include multiple cases to test.
const inputs = [
[{
"response_code": 1,
"response_message": [{
"a": 1000,
"b": 1000001,
"c": 10000002
}]
},
{
"response_code": 1,
"response_message": [{
"p": 1000,
"q": 1000001,
"r": 10000002
}]
}
],
[{
"response_code": 1,
"response_message": [{
"a": 1000,
"b": 1000001,
"c": 10000002
}]
},
{
"response_code": 1,
"response_message": 'No data'
}
],
[{
"response_code": 1,
"response_message": 'No data'
},
{
"response_code": 1,
"response_message": 'No data'
}
]
]
const getGroupedArr = (arr) => {
const codeMap = arr.reduce((cMap,obj) => {
let existingMessageArr = cMap.get(obj.response_code);
let arrayToAdd = Array.isArray(obj.response_message) ? obj.response_message : [];
if(existingMessageArr){
existingMessageArr.push(...arrayToAdd);
} else {
cMap.set(obj.response_code,arrayToAdd);
}
return cMap;
},new Map());
const iterator = codeMap[Symbol.iterator]();
const resultArr = [];
for (let item of iterator) {
resultArr.push({
response_code: item[0],
response_message: item[1]
})
}
return resultArr;
}
inputs.forEach((inputArr,index) => {
console.log(`Result for input ${index+1}`,getGroupedArr(inputArr));
})
Notice that I used Map where in JS most people prefer objects because maps in JS are iterable, but with an object I would've had to do an extra Object.keys() step, so this makes is slightly more efficient than the object approach, though a little more verbose.
Also note that in the third case, when no object with a particular response_code has any data, the result would be an empty array rather than a string. In weakly typed environments like JS, it is always a good practice to maintain some type consistency (which actually makes the input value of 'No data' in response_code not ideal), otherwise you may need to put type checks everywhere (like in the edited funciton in the above snippet).
Same function can be used in a contraint you mentioned in the comments, when the objects with same response_code exist in two different arrays (the two input arrays can simply be merged into one):
const inputArr1 = [{
"response_code": 1,
"response_message": [{
"a": 1000,
"b": 1000001,
"c": 10000002
}]
}]
const inputArr2 = [{
"response_code": 1,
"response_message": [{
"p": 1000,
"q": 1000001,
"r": 10000002
}]
}]
const getGroupedArr = (arr) => {
const codeMap = arr.reduce((cMap,obj) => {
let existingMessageArr = cMap.get(obj.response_code);
let arrayToAdd = Array.isArray(obj.response_message) ? obj.response_message : [];
if(existingMessageArr){
existingMessageArr.push(...arrayToAdd);
} else {
cMap.set(obj.response_code,arrayToAdd);
}
return cMap;
},new Map());
const iterator = codeMap[Symbol.iterator]();
const resultArr = [];
for (let item of iterator) {
resultArr.push({
response_code: item[0],
response_message: item[1]
})
}
return resultArr;
}
console.log(getGroupedArr([...inputArr1,...inputArr2]));
I think you're looking for a groupby.
Check this good old post from stackoverflow and be aware of the different answers/ implementaions:
Most efficient method to groupby on an array of objects
const arrayOf0 = yourArray.filter(item => item.response_code===0)
const arrayOf1 = yourArray.filter(item => item.response_code===1)
const merged0 = {response_code: 0, response_message: []};
const merged1 = {response_code: 1, response_message: []};
arrayOf0.forEach(item => {
merged0.response_message.push(item.response_message[0]
})
arrayOf1.forEach(item => {
merged1.response_message.push(item.response_message[0]
})
Something like this?
I have resolved the array merging by below code.
const mergedArray = [];
myarray.forEach((obj, index) => {
var newObj = {}
if(index > 0){
if(mergedArray.length > 0){
for(let i=0; i<obj.response_message.length;i++){
mergedArray[0].response_message.push(obj.response_message[i]);
}
}
}else{
newObj["response_code"] = obj.response_code ;
newObj["response_message"] = obj.response_message;
mergedArray.push(newObj);
}
})

Object destructing using ES6

I have this nested object that contains also an array.
result:{
"a": [{ "b": { "c": 1,
"d": 2,
"e": 3
},
"f": 0
}]
}
How can I destructure this object using ES6 if I need the value of d?
Object destructuring notation is just like object literal notation, just on the other side of the equals sign. So you write exactly what you'd write to only create the structure necessary for d, and put your d variable/constant name there.
It looks like you're showing us the contents of your object (e.g., it has a property called result), so:
const obj = {
result: {
"a": [{
"b": {
"c": 1,
"d": 2,
"e": 3
},
"f": 0
}]
}
};
const {
result: {
"a": [{
"b": {
d
}
}]
}
} = obj;
console.log(d);
I'm not saying I'd use destructuring here. In fact, I probably wouldn't. But you can. :-)
If result was the variable containing the object, just remove that layer:
const obj = {
"a": [{
"b": {
"c": 1,
"d": 2,
"e": 3
},
"f": 0
}]
};
const {
"a": [{
"b": {
d
}
}]
} = obj;
console.log(d);
Of course, that's taking your question at face value that you want the d from the first entry in a. You can generalize it to get entry n like this (I'm back to assuming result is part of the object):
const obj = {
result: {
"a": [
{
"b": {
"c": 1,
"d": 2,
"e": 3
},
"f": 0
},
{
"b": {
"c": 1,
"d": 4, // ***
"e": 3
},
"f": 0
}
]
}
};
const n = 1; // Which entry in `a` to get
const {
result: {
"a": {
[n]: {
"b": {
d
}
}
}
}
} = obj;
console.log(d);
I'm using object destructuring for a rather than array destructuring, with a computed property name. I can do that because arrays are objects.
Array destructuring notation is just like array literal notation, too. :-)
You want the value of d?
result.a[0].b.d ?

How to fill in missing keys in an Array of Objects?

I have an Array of Objects which should all have the same keys, but some of the keys are missing. I would like to fill in the missing keys with a generic value.
I am looking for a simple way to do that (natively or via a library), the code below I use now works, bit looks to my untrained eyes quite heavy and I am sure I reinvented the tedious way to do something while there is a simple one.
var arr = [{
"a": 1,
"b": 2,
"c": 3
},
{
"a": 10,
"c": 30
},
{
"b": 200,
"c": 300
},
]
// get the list of all keys
var allkeys = []
arr.forEach((objInArr) => {
allkeys = allkeys.concat(Object.keys(objInArr))
})
// check all arr entries for missing keys
arr.forEach((objInArr, i) => {
allkeys.forEach((key) => {
if (objInArr[key] === undefined) {
// the generic value, in this case 0
arr[i][key] = 0
}
})
})
console.log(arr)
Here is a version using property spread in object literals, although this will have very limited browser support:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator
var arr = [{
"a": 1,
"b": 2,
"c": 3
},
{
"a": 10,
"c": 30
},
{
"b": 200,
"c": 300
},
]
// Create an object with all the keys in it
// This will return one object containing all keys the items
let obj = arr.reduce((res, item) => ({...res, ...item}));
// Get those keys as an array
let keys = Object.keys(obj);
// Create an object with all keys set to the default value (0)
let def = keys.reduce((result, key) => {
result[key] = 0
return result;
}, {});
// Use object destrucuring to replace all default values with the ones we have
let result = arr.map((item) => ({...def, ...item}));
// Log result
console.log(result);
Your version is fine, although I would probably avoid all those array concat calls by just building up an object (or Set) with the keys. It's also a bit less clunky with for-of:
var arr = [{
"a": 1,
"b": 2,
"c": 3
},
{
"a": 10,
"c": 30
},
{
"b": 200,
"c": 300
},
];
// Get all the keys
const keyObj = Object.create(null);
for (const entry of arr) {
for (const key of Object.keys(entry)) {
keyObj[key] = true;
}
}
const allkeys = Object.keys(keyObj);
// Check all arr entries for missing keys
for (const entry of arr) {
for (const key of allkeys) {
if (entry[key] === undefined) { // ***I'd change this
entry[key] = 0;
}
}
}
console.log(arr);
.as-console-wrapper {
max-height: 100% !important;
}
Re *** I'd change this: Note that there's a difference between a property that exists and has the value undefined and a property that doesn't exist at all. Your code is treating them as the same thing. Of course, if you know they won't have the value undefined (for instance, because of the API you're getting them from)...
You can use Object.assign to merge each element with an object holding default key-values:
var arr = [{
"a": 1,
"b": 2,
"c": 3
},
{
"a": 10,
"c": 30
},
{
"b": 200,
"c": 300
},
];
var defaultObj = arr.reduce((m, o) => (Object.keys(o).forEach(key => m[key] = 0), m), {});
arr = arr.map(e => Object.assign({}, defaultObj, e));
console.log(arr);

Categories