Merge arrays from different objects with same key - javascript

I have the following code:
const blueData = {
"items": [
{
"id": 35,
"revision": 1,
"updatedAt": "2021-09-10T14:29:54.595012Z",
},
]
}
const redData = {}
const greenData = {
"items": [
{
"id": 36,
"revision": 1,
"updatedAt": "2021-09-10T14:31:07.164368Z",
}
]
}
let colorData = []
colorData = blueData.items ? [colorData, ...blueData.items] : colorData
colorData = redData.items ? [colorData, ...redData.items] : colorData
colorData = greenData.items ? [colorData, ...greenData.items] : colorData
I am guessing the spread operator is not the right approache here as I'm getting some extra arrays in my final colorData array. I simply want to build a single array of 'items' that contains all of the 'items' from the 3 objects.
Here's a link to that code in es6 console: https://es6console.com/ktkhc3j2/

Put your data into an array then use flatMap to unwrap each .items:
[greenData, redData, blueData].flatMap(d => d.items ?? [])
//=> [ {id: 36, revision: 1, updatedAt: '2021-09-10T14:31:07.164368Z'}
//=> , {id: 35, revision: 1, updatedAt: '2021-09-10T14:29:54.595012Z'}]
If you fancy you could abstract d => d.items ?? [] with a bit of curry (no pun intended ;)
const take = k => o => o[k] ?? [];
Which gives us:
[greenData, redData, blueData].flatMap(take('items'))
We can even go a step further if you ever need to repeat this process with different keys:
const concatBy = fn => xs => xs.flatMap(x => fn(x));
Now it almost feels like you're expressing your intent with words instead of code:
const takeItems = concatBy(take('items'));
takeItems([greenData, redData, blueData]);
//=> [ {id: 36, revision: 1, updatedAt: '2021-09-10T14:31:07.164368Z'}
//=> , {id: 35, revision: 1, updatedAt: '2021-09-
Let's build another function:
const takeFood = concatBy(take('food'));
takeFood([{food: ['🥑', '🥕']}, {food: ['🌽', '🥦']}]);
//=> ['🥑', '🥕', '🌽', '🥦']
Addendum
This is only meant as a potentially useful learning material. My advice is to use flatMap.
This:
[[1, 2], [3, 4]].flatMap(x => x)
//=> [1, 2, 3, 4]
Can also be expressed with reduce. Slightly more verbose but does what it says on the tin:
[[1, 2], [3, 4]].reduce((xs, x) => xs.concat(x), [])
//=> [1, 2, 3, 4]
So to put it simply you could also do:
[greenData, redData, blueData].reduce((xs, x) => xs.concat(x.items ?? []), [])

You can do this using the Logical OR operator which lets you provide a default value if the items field is missing.
const blueData = { items: [ { id: 35, revision: 1, updatedAt: '2021-09-10T14:29:54.595012Z', }, ], };
const redData = {};
const greenData = { items: [ { id: 36, revision: 1, updatedAt: '2021-09-10T14:31:07.164368Z', }, ], };
const colorData = [
...(blueData.items || []),
...(redData.items || []),
...(greenData.items || []),
];
console.log(colorData);

Maybe I'm a little old-fashioned but I'd use concat for that:
The concat() method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat
const blueData = {
"items": [
{
"id": 35,
"revision": 1,
"updatedAt": "2021-09-10T14:29:54.595012Z",
},
]
}
const redData = {}
const greenData = {
"items": [
{
"id": 36,
"revision": 1,
"updatedAt": "2021-09-10T14:31:07.164368Z",
}
]
}
const colorData = [].concat(blueData.items,redData.items,greenData.items).filter(x => x)
console.log(colorData)
the last filter is for removing undefined values

Like this?
colorData = blueData.items ? [...colorData, ...blueData.items] : colorData
colorData = redData.items ? [...colorData, ...redData.items] : colorData
colorData = greenData.items ? [...colorData, ...greenData.items] : colorData
Output:
[{"id":35,"revision":1,"updatedAt":"2021-09-10T14:29:54.595012Z"},
{"id":36,"revision":1,"updatedAt":"2021-09-10T14:31:07.164368Z"}]
I think you need to add the spread operator also to the colorData array, because if not you are adding the colorData array itself, not its items.

If you want the simplest solution, you can iterate with a for-loop between all arrays. Create a temporary array that will store data found on each index. This is the fastest and the most flexible solution.
var x1 = {
"items": [
{ "testKey1": "testVal" }
]
};
var x2 = {
"items": [
{ "testKey2.0": "testVal2" },
{ "testKey2.1": "testVal2" },
{ "testKey2.2": "testVal2" },
]
};
var x3 = {
"items": [
{ "testKey3.0": "testVal3" },
{ "testKey3.1": "testVal3" }
]
};
function combineArrays(...arrays) {
var tempArray = [];
for (let index in arrays) {
let currentArray = arrays[index];
for (let innerArrayIndex in currentArray) {
tempArray.push(currentArray[innerArrayIndex]);
}
}
return tempArray;
}
var result = combineArrays(x1.items, x2.items, x3.items);
console.log(result);
The solutions using a spread operator do not take into consideration that all the objects will be cloned using a shallow copy. Have a look.

Related

How to add objects to an array and avoid duplicate keys for an object?

I have these 2 arrays:
productsForSale = ['eggs', 'eggs', 'bread', 'milk'];
soldPrice = [2.70, 2.50, 1.97, 3.29];
yes, the values of the first 2 elements ("eggs") are different but that's meant to be for this question. Now I want to create an array of objects that will look like this:
[
{
eggs: 2.70
},
{
eggs: 2.50
},
{
bread: 1.97
},
{
milk: 3.29
}
]
So far I have this code:
var obj = {};
var arr = [];
productsForSale.forEach((key, i) => {
obj[key] = soldPrice[i];
arr.push(obj);
});
But I don't get the expected output. Can anyone point me in the right dirrection? Thanks
You can use map().
const productsForSale = ["eggs", "eggs", "bread", "milk"];
const soldPrice = [2.7, 2.5, 1.97, 3.29];
const output = productsForSale.map((el, i) => ({ [el]: soldPrice[i] }));
console.log(output);

Array.reduce and recursion

I am trying to nest a recursive function in an Array.reduce call.
But the following does not work for the third instance only
const i1 = {'local': [['a','b']]};
const i2 = {'local': [['c','d']], 'recursive': []};
const i3 = {'local': [['c','d']], 'recursive': [{'local': [['e','f']]}]};
function reduce(current, result = []) {
if(current.hasOwnProperty('local'))
result.push(...current.local);
if(current.hasOwnProperty('recursive'))
result = current.recursive.reduce(reduce, result);
//'result =' should be optional, but yields a wrong answer anyway
return result;
}
console.log(reduce(i1));
console.log(reduce(i2));
console.log(reduce(i3));
So instead of calling reduce I tried the following loop
for(var i = 0; i < current.recursive.length; ++i)
result = reduce(current.recursive[i], result);
//'result = ' is optional for it is passed by reference
and it works.
Being new to JavaScript, I am certain of missing a key feature here, so could you explain ?
Output for the third instance should be
[ [ 'c', 'd' ], [ 'e', 'f' ] ]
but is
{ local: [ [ 'e', 'f' ] ] }
or
[ [ 'c', 'd' ] ]
when result = is removed.
The problem is the order of the parameters, the first argument to the reduce callback function is the accumulator not the current value. Try this:
const i1 = {'local': [['a','b']]};
const i2 = {'local': [['c','d']], 'recursive': []};
const i3 = {'local': [['c','d']], 'recursive': [{'local': [['e','f']]}]};
function reduce(result, current) {
if(current.hasOwnProperty('local'))
result.push(...current.local);
if(current.hasOwnProperty('recursive'))
result = current.recursive.reduce(reduce, result);
//'result =' should be optional, but yields a wrong answer anyway
return result;
}
console.log(reduce([], i1));
console.log(reduce([], i2));
console.log(reduce([], i3));
This only covers the cases when local and recursive only includes one element, like in your example.
const i1 = { local: [["a", "b"]] };
const i2 = { local: [["c", "d"]], recursive: [] };
const i3 = { local: [["c", "d"]], recursive: [{ local: [["e", "f"]] }] };
const i4 = {
local: [["c", "d"]],
recursive: [{ local: [["e", "f"]], recursive: [{ local: [["g", "h"]] }] }]
};
const solution = ({ local: [firstItem], recursive }) =>
[firstItem].concat(
recursive && recursive.length ? solution(recursive[0]) : []
);
console.log(solution(i1));
console.log(solution(i2));
console.log(solution(i3));
console.log(solution(i4));
I think what you want to implement is flatten - It's no coincidence that it's a simple wrapper for Array.prototype.flatMap -
const i1 =
{ local: [[1,2]] }
const i2 =
{ local: [[3,4]], recursive: [] }
const i3 =
{ local: [[3,4]], recursive: [{ local: [[5,6]] }] }
const i4 =
{ local: [[1,2]],
recursive: [
{ local: [[3,4]] },
{ local: [[5,6]] , recursive: [{ local: [[7,8]] }] }] }
const flatten = ({ local = [], recursive = [] }) =>
[ ...local, ...recursive.flatMap(flatten) ]
console.log(flatten(i1))
// [ [ 1, 2 ] ]
console.log(flatten(i2))
// [ [ 3, 4 ] ]
console.log(flatten(i3))
// [ [ 3, 4 ], [ 5, 6 ] ]
console.log(flatten(i4))
// [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ], [ 7, 8 ] ]
The spread arguments can be traded for Array.prototype.concat, if that is preferred -
const flatten = ({ local = [], recursive = [] }) =>
[ ...local, ...recursive.flatMap(flatten) ]
local.concat(recursive.flatMap(flatten))
Array.prototype.flatMap is a special kind of Array.prototype.reduce -
const flatten = ({ local = [], recursive = [] }) =>
local.concat(
recursive.reduce((r, x) => r.concat(flatten(x)), [])
)
And since Array.prototype.concat is a pure operation, we can simplify it a bit more -
const flatten = ({ local = [], recursive = [] }) =>
recursive.reduce((r, x) => r.concat(flatten(x)), local)
And finally we see it again using reduce and array spread arguments -
const flatten = ({ local = [], recursive = [] }) =>
recursive.reduce((r, x) => [...r, ...flatten(x)], local)
Each of these flatten produce the exact same outputs and the input arrays are not mutated in the process. Hopefully this gives you a little insight on how the helpful Array.prototype.flatMap works.

filter data and map the only received data

I am facing a problem.
var arr = ['VBH', 'KTL', 'PVC', 'IF & AF', 'BC', 'CC&HC', 'UBS', 'FAD&DVD'];
var obj = [
{"materialTypeID":9,"name":"","abbreviation":"UBS","count":1,"duns":0,"plantId":0},
{"materialTypeID":18,"name":null,"abbreviation":"PVC","count":1,"duns":0,"plantId":0},
{"materialTypeID":7,"name":"","abbreviation":"FAD&DVD","count":4,"duns":0,"plantId":0}
];
and I want the result in sorting format as arr variable shows such as VBH object or KTL object if they found.
[
{"materialTypeID":18,"name":null,"abbreviation":"PVC","count":1,"duns":0,"plantId":0},
{"materialTypeID":9,"name":"","abbreviation":"UBS","count":1,"duns":0,"plantId":0},
{"materialTypeID":7,"name":"","abbreviation":"FAD&DVD","count":4,"duns":0,"plantId":0}
]
In java script, I want to implement.
Thanks,
Loop the arr and use filter to get the element from the obj. filter will return a new array, check if the length of this new array is more than 0 , then use push to put the element
var arr = ['VBH', 'KTL', 'PVC', 'IF & AF', 'BC', 'CC&HC', 'UBS', 'FAD&DVD']
var obj = [{
"materialTypeID": 9,
"name": "",
"abbreviation": "UBS",
"count": 1,
"duns": 0,
"plantId": 0
}, {
"materialTypeID": 18,
"name": null,
"abbreviation": "PVC",
"count": 1,
"duns": 0,
"plantId": 0
}, {
"materialTypeID": 7,
"name": "",
"abbreviation": "FAD&DVD",
"count": 4,
"duns": 0,
"plantId": 0
}]
let sortedArray = [];
arr.forEach(function(item) {
let getElem = obj.filter(function(items) {
return items.abbreviation === item
})
if (getElem.length > 0) {
sortedArray.push(getElem[0])
}
})
console.log(sortedArray)
It seems that you want to sort by decreasing materialTypeID.
Your problem has already been resolved there : Javascript object list sorting by object property
obj.sort(function(a, b) {
return b.materialTypeID - a.materialTypeID;
})

convert array value to an object value

How do I convert this array
[ { africa: 1 },
{ culture: 1 },
{ feminism: 3 },
{ 'feminists rising': 1 },
{ law: 1 } ]
into something like this
someObj = {africa: 1, culture: 1, feminism: 3, 'feminists rising': 1, law: 1}
Spread the array into Object#assign:
const data = [{"africa":1},{"culture":1},{"feminism":3},{"feminists rising":1},{"law":1}];
const result = Object.assign({}, ...data);
console.log(result);
const raw = [
{
africa: 1
},
{
culture: 1
},
{
feminism: 3
},
{
'feminists rising': 1
},
{
law: 1
}
];
console.log(raw.reduce((acc, next) => {
const key = Object.keys(next)[0];
acc[key] = next[key];
return acc;
}, {}));
const subjects = [
{africa: 1},
{culture: 1},
{feminism: 3},
{'feminists rising': 1},
{law: 1}
];
Use the Object.assign() method to mash a list of the individual objects into an empty array. In order to generate the list from the array of objects, the spread syntax (...) is used.
const obj = Object.assign({}, ...subjects);

JavaScript flattening an array of arrays of objects

I have an array which contains several arrays, each containing several objects, similar to this.
[[object1, object2],[object1],[object1,object2,object3]]
Here is a screenhot of the object logged to the console.
What would be the best approach to flattening this out so it just an array of objects?
I've tried this with no luck:
console.log(searchData);
var m = [].concat.apply([],searchData);
console.log(m);
searchData logs out the screenshot above, but m logs out [ ]
Here is the actual contents of searchData:
[[{"_id":"55064111d06b96d974937a6f","title":"Generic Title","shortname":"generic-title","contents":"<p>The Healing Center offers practical, social, and spiritual support to individuals and families. Services include, but are not limited to: food and clothing, job skills training and job search assistance, auto repair (Saturdays only), mentoring, financial counseling, tutoring, prayer, life skills training, and helpful information about local community services.</p><p>Stay in touch with us:</p>","__v":0},{"_id":"5508e1405c621d4aad2d2969","title":"test english","shortname":"test-page","contents":"<h2>English Test</h2>","__v":0}],[{"_id":"550b336f33a326aaee84f883","shortname":"ok-url","title":"now english","contents":"<p>okokko</p>","category":"Transportation","__v":0}]]
You can use Array.concat like bellow:-
var arr = [['object1', 'object2'],['object1'],['object1','object2','object3']];
var flattened = [].concat.apply([],arr);
flattened will be your expected array.
ES 2020 gives the flat, also flatMap if you want to iterate over, to flat lists of lists:
[['object1'], ['object2']].flat() // ['object1', 'object2']
A recursive solution for deep (nested) flattening:
function flatten(a) {
return Array.isArray(a) ? [].concat.apply([], a.map(flatten)) : a;
}
A bit more compactly with ES6:
var flatten = a => Array.isArray(a) ? [].concat(...a.map(flatten)) : a;
For fun, using a generator named F for "flatten", to lazily generate flattened values:
function *F(a) {
if (Array.isArray(a)) for (var e of a) yield *F(e); else yield a;
}
>> console.log(Array.from(F([1, [2], 3])));
<< [ 1, 2, 3 ]
For those not familiar with generators the yield * syntax yields values from another generator. Array.from takes an iterator (such as results from invoking the generator function) and turns it into an array.
If you only need simple flatten, this may works:
var arr = [['object1', 'object2'],['object1'],['object1','object2','object3']];
var flatenned = arr.reduce(function(a,b){ return a.concat(b) }, []);
For more complex flattening, Lodash has the flatten function, which maybe what you need: https://lodash.com/docs#flatten
//Syntax: _.flatten(array, [isDeep])
_.flatten([1, [2, 3, [4]]]);
// → [1, 2, 3, [4]];
// using `isDeep` to recursive flatten
_.flatten([1, [2, 3, [4]]], true);
// → [1, 2, 3, 4];
you can use flat() :
const data = [ [{id:1}, {id:2}], [{id:3}] ];
const result = data.flat();
console.log(result);
// you can specify the depth
const data2 = [ [ [ {id:1} ], {id:2}], [{id:3}] ];
const result2 = data2.flat(2);
console.log(result2);
in your case :
const data = [[{"_id":"55064111d06b96d974937a6f","title":"Generic Title","shortname":"generic-title","contents":"<p>The Healing Center offers practical, social, and spiritual support to individuals and families. Services include, but are not limited to: food and clothing, job skills training and job search assistance, auto repair (Saturdays only), mentoring, financial counseling, tutoring, prayer, life skills training, and helpful information about local community services.</p><p>Stay in touch with us:</p>","__v":0},{"_id":"5508e1405c621d4aad2d2969","title":"test english","shortname":"test-page","contents":"<h2>English Test</h2>","__v":0}],[{"_id":"550b336f33a326aaee84f883","shortname":"ok-url","title":"now english","contents":"<p>okokko</p>","category":"Transportation","__v":0}]]
const result = data.flat();
console.log(result);
Using ES6 Spread Operator
Array.prototype.concat(...searchData)
OR
[].concat(...searchData)
You can use this custom recursive method to flattened any nested array
const arr = [
[1, 2],
[3, 4, 5],
[6, [7, 8], 9],
[10, 11, 12]
]
const flatenedArray = arr => {
let result = [];
if(!arr.constructor === Array) return;
arr.forEach(a => {
if(a.constructor === Array) return result.push(...flatenedArray(a));
result.push(a);
});
return result;
}
console.log(flatenedArray(arr)); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
Recursively flatten an array:
function flatten(array) {
return !Array.isArray(array) ? array : [].concat.apply([], array.map(flatten));
}
var yourFlattenedArray = flatten([[{"_id":"55064111d06b96d974937a6f","title":"Generic Title","shortname":"generic-title","contents":"<p>The Healing Center offers practical, social, and spiritual support to individuals and families. Services include, but are not limited to: food and clothing, job skills training and job search assistance, auto repair (Saturdays only), mentoring, financial counseling, tutoring, prayer, life skills training, and helpful information about local community services.</p><p>Stay in touch with us:</p>","__v":0},{"_id":"5508e1405c621d4aad2d2969","title":"test english","shortname":"test-page","contents":"<h2>English Test</h2>","__v":0}],[{"_id":"550b336f33a326aaee84f883","shortname":"ok-url","title":"now english","contents":"<p>okokko</p>","category":"Transportation","__v":0}]]
);
log(yourFlattenedArray);
function log(data) {
document.write('<pre>' + JSON.stringify(data, null, 2) + '</pre><hr>');
}
* {font-size: 12px; }
let functional = {
flatten (array) {
if (Array.isArray(array)) {
return Array.prototype.concat(...array.map(this.flatten, this));
}
return array;
}
};
functional.flatten([0, [1, 2], [[3, [4]]]]); // 0, 1, 2, 3, 4
I've noticed that people are using recursions which are not cost friendly, especially with new ES6 standards giving us the power of spread operators. When you're pushing the items into the master array just use ... and it will automatically add flattened objects. Something like
array.push(...subarray1) // subarray1 = [object1, object2]
array.push(...subarray2) // subarray2 = [object3]
array.push(...subarray3) // subarray3 = [object4,object5, object6]
// output -> array = [object1, object2, object3, object4, object5, object6]
My solution to flatten an array of objects and return a single array.
flattenArrayOfObject = (arr) => {
const flattened = {};
arr.forEach((obj) => {
Object.keys(obj).forEach((key) => {
flattened[key] = obj[key];
});
});
return flattened;
};
Example
const arr = [
{
verify: { '0': 'xyzNot verified', '1': 'xyzVerified' },
role_id: { '1': 'xyzMember', '2': 'xyzAdmin' },
two_factor_authentication: { '0': 'No', '1': 'Yes' }
},
{ status: { '0': 'xyzInactive', '1': 'Active', '2': 'xyzSuspend' } }
]
flattenArrayOfObject(arr)
// {
// verify: { '0': 'xyzNot verified', '1': 'xyzVerified' },
// status: { '0': 'xyzInactive', '1': 'Active', '2': 'xyzSuspend' },
// role_id: { '1': 'xyzMember', '2': 'xyzAdmin' },
// two_factor_authentication: { '0': 'No', '1': 'Yes' }
// }
If each object has an array and continues in the same way nested :
function flatten(i,arrayField){
if(Array.isArray(i)) return i.map(c=>flatten(c,arrayField));
if(i.hasOwnProperty(arrayField)) return [{...i,[arrayField]:null},...i[arrayField].map(c=>flatten(c,arrayField))];
return {...i,[arrayField]:null};
}
let data=flatten(myData,'childs');
mydata like this :
[
{
"id": 1,
"title": "t1",
"sort_order": 200,
"childs": [
{
"id": 2,
"title": "t2",
"sort_order": 200,
"childs": []
},
{
"id": 3,
"title":"mytitle",
"sort_order": 200,
"childs": []
},
{
"id": 4,
"title":"mytitle",
"sort_order": 200,
"childs": []
},
{
"id": 5,
"title":"mytitle",
"sort_order": 200,
"childs": []
},
{
"id": 6,
"title":"mytitle",
"sort_order": 200,
"childs": []
}
]
},
{
"id": 7,
"title": "راهنما",
"sort_order":"mytitle",
"childs": [
{
"id": 8,
"title":"mytitle",
"sort_order": 200,
"childs": []
},
{
"id": 9,
"title":"mytitle",
"sort_order": 200,
"childs": []
},
{
"id": 10,
"title":"mytitle",
"sort_order": 200,
"childs": []
}
]
}
]
let nestedArray = [[1, 2], [3, 4], [5, 6]];
let flattenArray = function(nestedArray) {
let flattenArr = [];
nestedArray.forEach(function(item) {
flattenArr.push(...item);
});
return flattenArr;
};
console.log(flattenArray(nestedArray)); // [1, 2, 3, 4, 5, 6]
var arr = [1,[9,22],[[3]]];
var res = [];
function flatten(arr){
for(let i=0;i<arr.length;i++){
if(typeof arr[i] == "number"){
res.push(arr[i]);
}
else if(typeof arr[i] == "object"){
fatten(arr[i]);
}
}
}
Calling function
flatten(arr);
console.log(res);
Result
 
[1, 9, 22, 3]
// Polyfill flat method
var flatten = a => Array.isArray(a) ? [].concat(...a.map(flatten)) : a;
var deepFlatten = (arr, depth = 1) => {
return depth > 0 ? arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? deepFlatten(val, depth - 1) : val), [])
: arr.slice();
}
console.log(deepFlatten([0, 1, 2, [[[3, 4]]]], Infinity));
// You can pass label in place of 'Infinity'

Categories

Resources