Pivot data using lodash - javascript

I need to convert the the init array to final array preferably using lodash.
initArray = [
{
"date":"2017-08-15",
"data":[
{
"color":"orange",
"count":100
},
{
"color":"green",
"count":101
}
]
},
{
"date":"2017-08-14",
"data":[
{
"color":"orange",
"count":102
},
{
"color":"green",
"count":103
}
]
}
]
finalArray = [
{
"color":"orange",
"data":[
100,
102
]
},
{
"color":"green",
"data":[
101,
103
]
}
]

This way seems like the lodash calls make sense to me.
// var _ = require('lodash')
initArray = [
{
"date":"2017-08-15",
"data":[
{
"color":"orange",
"count":100
},
{
"color":"green",
"count":101
}
]
},
{
"date":"2017-08-14",
"data":[
{
"color":"orange",
"count":102
},
{
"color":"green",
"count":103
}
]
}
]
result = _(initArray)
//.map('data')
//.flatten()
.flatMap('data') // instead of .map('data').flatten()
.groupBy('color')
.map((item, key) => ({
color: key,
count: _.map(item, 'count')
}))
.value()
console.log(result)
<script src="https://cdn.jsdelivr.net/lodash/4/lodash.min.js"></script>

you can use reduce to flatten the original array, so that all data arrays are on same level. Then use _.transform to get a temp object mapping colors to array of their counts. and then you can push things to finalArray using forEach.
var initArray = [
{
"date":"2017-08-15",
"data":[
{
"color":"orange",
"count":100
},
{
"color":"green",
"count":101
}
]
},
{
"date":"2017-08-14",
"data":[
{
"color":"orange",
"count":102
},
{
"color":"green",
"count":103
}
]
}
];
var finalArray = [];
var temp = _.transform(initArray.reduce((a,b) => a.data.concat(b.data)),
(r, v) => (r[v.color] || (r[v.color] = [])).push(v.count), {});
_.forEach(temp, (v,k) => finalArray.push({color:k, count:v}));
console.log(finalArray);
<script src="https://cdn.jsdelivr.net/lodash/4/lodash.min.js"></script>

Related

How to flatten nested array of objects and create new array

I have a data structure as the following
[
{
"models":[
{
"name":"xyz",
"options":[
{
"label":"blue"
},
{
"label":"brown"
},
]
},
{
"name":"abc",
"options":[
{
"label":"yellow"
}
]
},
{
"name":"def",
"options":[
{
"label":"green"
}
]
}
]
}
]
The end result should be an array with all of the labels and name like xyz: blue, xyz: brown, abc: yellow, def: green
so something like this
['xyz: blue', 'xyz: brown', 'abc: yellow','def: green']
I'm trying different approaches, one with RxJS operators and another with reduce
let flat = (acc, current) => {
}
models.reduce(flat, [])
You can use a reduce and a map like this.
const arr = [
{
"models":[
{
"name":"xyz",
"options":[
{
"label":"blue"
},
{
"label":"brown"
},
]
},
{
"name":"abc",
"options":[
{
"label":"yellow"
}
]
},
{
"name":"def",
"options":[
{
"label":"green"
}
]
}
]
}
];
const result = arr[0].models.reduce(
(acc, model) => [...acc, ...model.options.map(i => ({ [model.name]: i.label }))]
, []
);
console.log(result);
If the top level array can have multiple items rather than arr[0] you would need to add another reduce feeding it's accumulator in to the second reduce as it's starting accumulator rather than the empty starting array.
const arr = [
{
"models":[
{
"name":"xyz",
"options":[
{
"label":"blue"
},
{
"label":"brown"
},
]
},
{
"name":"abc",
"options":[
{
"label":"yellow"
}
]
},
{
"name":"def",
"options":[
{
"label":"green"
}
]
}
]
},
{
"models":[
{
"name":"ghi",
"options":[
{
"label":"gold"
},
{
"label":"pink"
},
]
}
]
}
];
const result = arr.reduce(
(acc, item) =>
item.models.reduce(
(acc2, model) => [...acc2, ...model.options.map((i) => ({ [model.name]: i.label }))]
, acc
),
[]
);
console.log(result);
Not sure where RxJs comes into this question but if you are looking to transform an object like this that comes back from a http request you would pipe it into the map operator and then use this function inside the map. If you are looking to do a reduce on a stream there is a reduce operator that emits the accumulator when the source stream completes or the scan operator that emits the accumulator each time the source emits.
Use nested calls to flatMap(), and in the innermost call you concatenate the model name with the option label.
const data = [{
"models": [{
"name": "xyz",
"options": [{
"label": "blue"
},
{
"label": "brown"
},
]
},
{
"name": "abc",
"options": [{
"label": "yellow"
}]
},
{
"name": "def",
"options": [{
"label": "green"
}]
}
]
}];
let result = data.flatMap(d => d.models.flatMap(model => model.options.map(option => `${model.name}: ${option.label}`)));
console.log(result);
Here is using multiple forEach and destructuring
const flat = (arr, res = []) => {
arr.forEach(({ models }) =>
models.forEach(({ name, options }) =>
options.forEach(({ label }) => res.push({ [name]: label }))
)
);
return res;
};
const data = [
{
models: [
{
name: "xyz",
options: [
{
label: "blue",
},
{
label: "brown",
},
],
},
{
name: "abc",
options: [
{
label: "yellow",
},
],
},
{
name: "def",
options: [
{
label: "green",
},
],
},
],
},
];
console.log(flat(data));
const response = array[0].models.reduce((initial, model) => {
if (model.options.length === 1)
initial.push(`${model.name}: ${model.options[0].label}`);
else {
model.options.forEach((option) =>
initial.push(`${model.name}: ${option.label}`),
);
}
return initial;
}, []);
console.log(response)
;
// there is no need if the inner option has just one object you can just access it by model.options[0].label, that why there is a check to see if it one

How to filter array and create new array with specific object keys

I have an array with objects and in each object there is an "items" array. My goal is to combine these "items" into one array.
Goal / Expected Output
[
{ id: 'SUV' },
{ id: 'Compact' },
{ id: 'Gasoline' },
{ id: 'Hybrid' }
]
Sample Array
[
{
"id":"carType",
"items":[
{
"id":"SUV"
},
{
"id":"Compact"
}
]
},
{
"id":"fuelType",
"items":[
{
"id":"Gasoline"
},
{
"id":"Hybrid"
}
]
}
]
You could use Array#flatMap.
const data = [{"id":"carType","items":[{"id":"SUV"},{"id":"Compact"}]},{"id":"fuelType","items":[{"id":"Gasoline"},{"id":"Hybrid"}]}];
const r = data.flatMap(({ items }) => items);
console.log(r);
A one-liner in vanilla JS (earlier than EMCAScript 2019, flatMap is not available, so in case of that...)
[].concat(...arr.map(elt => elt.items))
Something like this?
newArr = []
for (let i = 0;i<arr.length;i++) {
for (let ii = 0;ii<arr[i].items.length;ii++) {
newArr.push(arr[i].items[ii].id)
}
}
console.log(newArr)

Efficient Way to Iterate and Producing Output in Javascript/React

I have following datas,
let response =[
{
"14714733": [
"Android Testing-1",
"Test special manual",
"Test Manual",
"SECESC"
]
},
{
"10110133": [
"Android Testing-1",
"SECESC"
]
}
]
let shipment =[
{
"masterDocumentNumber": "14714733"
},
{
"masterDocumentNumber": "10110133",
}
]
And
let flagIns=[
{
"fieldValue": "SECESC",
"fieldDescription": "Security Escort"
},
{
"fieldValue": "INS",
"fieldDescription": "Inspection"
}
]
How to iterate and add Corresponding response data in to shipment data as follows,
Desired output
let shipment =[
{
"masterDocumentNumber": "14714733",
"instructions":[
{"index":0,"instruction":"Android Testing-1"},
{"index":1,"instruction":"Test special manual"},
{"index":2,"instruction":"Test Manual"},
{"index":3,"instruction":"Security Escort"}
]
},
{
"masterDocumentNumber": "10110133",
"instructions":[
{"index":0,"instruction":"Android Testing-1"},
{"index":1,"instruction":"Security Escort"}
]
}
]
Note that if flagIns has same data in response then it need to be replaced with it's description.
You should be able to use a function similar to this.. it appears you just need to match up keys and values from the different objects..
function aggregate(response, shipment, flagIns) {
return shipment.map(({ masterDocumentNumber }) => {
let output = { masterDocumentNumber, instructions: [] }
let res = response.find(r => masterDocumentNumber in r);
if (res) {
res[masterDocumentNumber].forEach((r, i) => {
let ins = flagIns.find(fi => fi.fieldValue === r);
output.instructions.push({
index: i,
instruction: ins ? ins.fieldDescription : r
})
})
}
return output;
});
}
const response = [
{
"14714733": [
"Android Testing-1",
"Test special manual",
"Test Manual",
"SECESC"
]
},
{
"10110133": ["Android Testing-1", "SECESC"]
}
];
const shipment = [
{
masterDocumentNumber: "14714733"
},
{
masterDocumentNumber: "10110133"
}
];
const flagIns = [
{
fieldValue: "SECESC",
fieldDescription: "Security Escort"
},
{
fieldValue: "INS",
fieldDescription: "Inspection"
}
];
console.log(aggregate(response, shipment, flagIns));
let shipment =[];
//create array
response.map((res)=>{
//get keys
let key=Object.keys(res)
//loop in instructions
let instructions=[];
res[key].map((val,i)=>{
let inst ={
"index":i,
"instruction":val
}
instructions.push(inst)
})
let m={
"masterDocumentNumber":key,
"instructions":instructions
}
shipment.push(m)
})
console.log(JSON.stringify(shipment))
First flatten the response and flagIns array of objects and then iterate over the shipment array to get the desired output.
let response =[
{
"14714733": [
"Android Testing-1",
"Test special manual",
"Test Manual",
"SECESC"
]
},
{
"10110133": [
"Android Testing-1",
"SECESC"
]
}
]
let shipment =[
{
"masterDocumentNumber": "14714733"
},
{
"masterDocumentNumber": "10110133",
}
]
let flagIns=[
{
"fieldValue": "SECESC",
"fieldDescription": "Security Escort"
},
{
"fieldValue": "INS",
"fieldDescription": "Inspection"
}
]
const responseRes = response.reduce(function (acc, item) {
return Object.assign(acc, item);
}, {});
// responseRes
// {
// '10110133': [ 'Android Testing-1', 'SECESC' ],
// '14714733': [
// 'Android Testing-1',
// 'Test special manual',
// 'Test Manual',
// 'SECESC'
// ]
// }
const flagInsRes = flagIns.reduce(function (acc, item) {
return Object.assign(acc, {
[item.fieldValue]: item.fieldDescription});
}, {});
// flagInsRes
// { SECESC: 'Security Escort', INS: 'Inspection' }
const shipmentRes = shipment.map(obj => {
const temp = {};
temp.masterDocumentNumber = obj.masterDocumentNumber
temp.instructions = responseRes[obj.masterDocumentNumber]
.map((item, index) => {
return {
"index":index,
"instruction":flagInsRes[item] ? flagInsRes[item] : item}
});
return temp;
});
console.log(shipmentRes);

array to nested array conversion

my backend service send me list of node as an array. but I need is, each next node is value of its previous node(SEE EXAMPLE). I want whole list as nested object in singe object.
WHAT I HAVE:
[
{
"nodeId": 1,
},
{
"nodeId": 3,
},
{
"nodeId": 16,
}
]
WHAT I NEED:
[
{
"nodeId": 1,
"staticChild": [
{
"nodeId": 3,
"staticChild": [
{
"nodeId": 16,
}
]
}
]
}
]
You could reduce the array from the right side and build a new object with a staticChild property.
var array = [{ nodeId: 1 }, { nodeId: 3 }, { nodeId: 16 }],
result = array.reduceRight((a, b) => ({ ...b, staticChild: [a] }));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Based on the input / output you provided, you can use a recursive funtion like :
const data = [{
nodeId: 1
},
{
nodeId: 3
},
{
nodeId: 16
}
];
const transform = data => {
const [node, ...rest] = data;
if (rest.length > 0) {
return {
...node,
staticChild: [transform(rest)]
};
} else {
return {
...node,
hasChildren: false
};
}
};
const result = transform(data);
console.log(result);
At first reverse the array and the make an iteration over the revered array using reduce() to make your desire format.
let data = [{"nodeId": 1},{"nodeId": 3},{"nodeId": 16}]
data = data.reverse().reduce((old, cur) => {
if (!old.length) {
old = [cur]
} else {
cur['staticChild'] = old
old = [cur]
}
return old
}, [])
console.log(data)
You can use the reduceRight() array method to perform the transformation.
const data = [{
"nodeId": 1,
},
{
"nodeId": 3,
},
{
"nodeId": 16,
}
]
const nested = data.reduceRight((acc, item) => {
return [ { ...item, staticChild: acc } ]
}, []);
console.log(nested);
Or more succinctly:
const nested = data.reduceRight((acc, item) => [ { ...item, staticChild: acc } ],[]);

Filter objects using filter

I have array of object like below:
pages= [
{
"id":1,
"name":"name1",
"languages":[
{
"id":1,
"lang":"en"
},
{
"id":2,
"lang":"de"
}
]
},
{
"id":2,
"name":"name2",
"languages":[
{
"id":1,
"lang":"en"
},
{
"id":2,
"lang":"de"
}
]
},
{
"id":3,
"name":"name3",
"languages":[
{
"id":1,
"lang":"en"
}
]
}
]
And array of languages(these will be set by clicking on checkboxes) is as below:
selectedLanguages=['en'];
Now, I want to filter the main array depending upon the selectedLanguages values. I have tried with below code:
pages.filter(page => {
var present = false;
page.languages.map(l => {
if(selectedLanguages.includes(l.lang)) {
present = true;
}
});
if(present) {
return page;
}
})
Desired output: if selectedLanguages = ['en'] then all items from pages, if de then first 2 elements.
It works but I am curious if I can make it better?
Any suggestions would be helpful :) Thanks.
You can use a combination of Array#Filter, Array#find and Array#includes for that :
let pages= [
{
"id":1,
"name":"name1",
"languages":[
{
"id":1,
"lang":"en"
},
{
"id":2,
"lang":"de"
}
]
},
{
"id":2,
"name":"name2",
"languages":[
{
"id":1,
"lang":"en"
},
{
"id":2,
"lang":"fr"
}
]
}
]
let selectedLanguages=['fr'];
let result = pages.filter(e => e.languages.find(l => selectedLanguages.includes(l.lang)));
console.log(result);
Rather than trying to create an intermediate array with .map, it would be better to simply check if some of the languages include a lang matching the selectedLanguages:
const pages=[{"id":1,"name":"name1","languages":[{"id":1,"lang":"en"},{"id":2,"lang":"de"}]},{"id":2,"name":"name2","languages":[{"id":1,"lang":"en"},{"id":2,"lang":"de"}]}]
const selectedLanguages=['en'];
console.log(
pages.filter(({ languages }) => (
languages.some(({ lang }) => selectedLanguages.includes(lang))
))
)
You can use filter() with indexOf() check for the selectedLanguages array:
var pages= [
{
"id":1,
"name":"name1",
"languages":[
{
"id":1,
"lang":"en"
},
{
"id":2,
"lang":"de"
}
]
},
{
"id":2,
"name":"name2",
"languages":[
{
"id":1,
"lang":"en"
},
{
"id":2,
"lang":"de"
}
]
},
{
"id":3,
"name":"name3",
"languages":[
{
"id":5,
"lang":"us"
},
{
"id":6,
"lang":"de"
}
]
}
];
var selectedLanguages=['en'];
var res = pages.filter((page) => {
var languageFound = page.languages.find(language => selectedLanguages.indexOf(language.lang) !== -1);
if(languageFound){
return page;
}
});
console.log(res);

Categories