ES6 reduce array of objects with conditions - javascript

I'm stucked in a (in my opinion) complex reduce method.
Given is an array of objects.
const data =
[
{
"key" : "test1",
"value" : 32,
"type" : "OUT"
},
{
"key" : "test1",
"value" : 16,
"type" : "OUT"
},
{
"key" : "test1",
"value" : 8,
"type" : "IN"
},
{
"key" : "test2",
"value" : 32,
"type" : "OUT"
},
{
"key" : "test2",
"value" : 16,
"type" : "IN"
},
{
"key" : "test2",
"value" : 8,
"type" : "OUT"
},
];
I want to get the sum of values of each object grouped by key attribute. There are two type attributes (IN, OUT) where OUT should be interpreted as negative value.
So in the example above, I'm expecting following result object:
//-32 - 16 + 8 = -40
{
"key" : "test1",
"value" : -40,
"type" : "-"
},
//-32 + 16 - 8 = -24
{
"key" : "test2",
"value" : -24,
"type" : "-"
},
I'm grouping the data using the groupBy function of this SO answer.
Now I'm trying to get the sum using reduce with a filter, like in this SO answer.
However, it delivers me the wrong sums (16 and 8) + since I use filter - only one type is considered.
Here is my code:
const data =
[
{
"key" : "test1",
"value" : 32,
"type" : "OUT"
},
{
"key" : "test1",
"value" : 16,
"type" : "OUT"
},
{
"key" : "test1",
"value" : 8,
"type" : "IN"
},
{
"key" : "test2",
"value" : 32,
"type" : "OUT"
},
{
"key" : "test2",
"value" : 16,
"type" : "IN"
},
{
"key" : "test2",
"value" : 8,
"type" : "OUT"
},
];
//group by key
const groupBy = function(xs, key) {
return xs.reduce(function(rv, x) {
(rv[x[key]] = rv[x[key]] || []).push(x);
return rv;
}, {});
};
const grouped = groupBy(data,"key");
for (const [key, value] of Object.entries(grouped))
{
let x = value.filter(({type}) => type === 'OUT')
.reduce((sum, record) => sum + record.value)
console.log(x);
}
//const filtered = grouped.filter(({type}) => type === 'OUT');
console.log(Object.values(grouped));
Question 1:
Why does the reduce give me the wrong sum for type OUT?
Question 2:
Is there a way to consider both types (IN, OUT) without doing the same procedure again?

You can combine the grouping + counting in 1 reduce() if you set the default value to 0, you can always add (or remove) the value from the current key (type)
const data = [{"key" : "test1", "value" : 32, "type" : "OUT"}, {"key" : "test1", "value" : 16, "type" : "OUT"}, {"key" : "test1", "value" : 8, "type" : "IN"}, {"key" : "test2", "value" : 32, "type" : "OUT"}, {"key" : "test2", "value" : 16, "type" : "IN"}, {"key" : "test2", "value" : 8, "type" : "OUT"}, ];
const res = data.reduce((p, c) => {
(p[c['key']] = p[c['key']] || { ...c, value: 0 });
p[c['key']].value =
(c.type === 'IN')
? (p[c['key']].value + c.value)
: (p[c['key']].value - c.value);
return p;
},{});
console.log(res)
Output:
{
"test1": {
"key": "test1",
"value": -40,
"type": "OUT"
},
"test2": {
"key": "test2",
"value": -24,
"type": "OUT"
}
}

I would break this into two problems:
How to reduce each data value (reduce)
How to evaluate existing/new values (switch)
This way your code is less-coupled and it affords you with greater extensibility. Adding a new operator is as simple as adding a new case in the switch.
const reduceValue = (type, existingValue, newValue) => {
switch (type) {
case 'IN' : return existingValue + newValue;
case 'OUT' : return existingValue - newValue;
default : return existingValue; // or throw new Error(`Unsupported type: ${type}`)
}
};
const processValues = (data) =>
data.reduce((acc, { key, type, value }) => {
acc[key] ??= { key, type: '-', value: 0 };
acc[key].value = reduceValue(type, acc[key].value, value);
return acc;
},{});
const testData = [
{ "key" : "test1", "value" : 32, "type" : "OUT" },
{ "key" : "test1", "value" : 16, "type" : "OUT" },
{ "key" : "test1", "value" : 8, "type" : "IN" },
{ "key" : "test2", "value" : 32, "type" : "OUT" },
{ "key" : "test2", "value" : 16, "type" : "IN" },
{ "key" : "test2", "value" : 8, "type" : "OUT" }
];
console.log(processValues(testData))
.as-console-wrapper { top: 0; max-height: 100% !important; }

I would create 2 functions for applying the sign and store them in a variable.
const applySign = { "IN": nr => +nr, "OUT": nr => -nr };
Then do a simple for...of loop (with object destructuring). If there is no running total at the moment for the current key, set the initial value to 0 (using nullish coalescing assignment ??=). Finally add the current value with applied sign to the running total.
const sums = {};
for (const { key, value, type } of data) {
sums[key] ??= 0;
sums[key] += applySign[type](value);
}
const data = [
{ key: "test1", value: 32, type: "OUT" },
{ key: "test1", value: 16, type: "OUT" },
{ key: "test1", value: 8, type: "IN" },
{ key: "test2", value: 32, type: "OUT" },
{ key: "test2", value: 16, type: "IN" },
{ key: "test2", value: 8, type: "OUT" },
];
const applySign = { "IN": nr => +nr, "OUT": nr => -nr };
const sums = {};
for (const { key, value, type } of data) {
sums[key] ??= 0;
sums[key] += applySign[type](value);
}
console.log(sums);
With a few simple tweaks you can change the above in the output you're looking for:
const sums = {};
for (const { key, value, type } of data) {
sums[key] ??= { key, value: 0 };
sums[key].value += applySign[type](value);
}
const expected = Object.values(sums);
This gives you the base answer, though the type properties that you expect are currently missing. To add them you'll have to do another loop and check the final sum result.
for (const sum of expected) {
sum.type = sum.value < 0 ? "-" : "+";
}
const data = [
{ key: "test1", value: 32, type: "OUT" },
{ key: "test1", value: 16, type: "OUT" },
{ key: "test1", value: 8, type: "IN" },
{ key: "test2", value: 32, type: "OUT" },
{ key: "test2", value: 16, type: "IN" },
{ key: "test2", value: 8, type: "OUT" },
];
const applySign = { "IN": nr => +nr, "OUT": nr => -nr };
const sums = {};
for (const { key, value, type } of data) {
sums[key] ??= { key, value: 0 };
sums[key].value += applySign[type](value);
}
const expected = Object.values(sums);
console.log(expected);
// add type based on the value sign (don't know why)
for (const sum of expected) {
sum.type = sum.value < 0 ? "-" : "+";
}
console.log(expected);
If type is a static "-" and was not supposed to depend on the sign of value, then you can add it when you initially create the sum object.
sums[key] ??= { key, value: 0, type: "-" };

Related

Filtering array of object using key in Javascript (node.js)

Please anyone can help, I want to filter data based on key in node.js, I've tried most of things, but didn't worked.
Below example data which needs to be filtered. I could able to filter key:value pair, nested Json object but filtering Array of json object is not working.
E.g. var data =
[{
"createdBy":"Tom"
"logistics" : 0,
"paymentStatus" : "Paid",
"orderAmount" : 5393.75,
"details" : {
"street": "S.S road",
"postCOde": "440111",
},
"subOrders" : [
{
"name" : "sub Product1",
"mrp" : 12,
"details": "desk"
},
{
"name" : "subProduct2",
"mrp" : 89,
"details": "chair"
}
]
}]
result object should be filtered based on given permission array
var permissionArray = ['logistics','paymentStatus','details.street','subOrders.name','subOrders.details'];
filtered result should look like
{
result = [{
"logistics" : 0,
"paymentStatus" : "Paid",
"details" : {
"street": "S.S road",
},
"subOrders" : [
{
"name" : "sub Product1",
"details" : "desk"
},
{
"name" : "sub Product1",
"details" : "chair"
}
]
}]
}
Building on #radulle's answer to allow the 'details.name' dot notation desired:
(Also updated to now allow multiple subfields of one field - like 'subOrders.name' and 'subOrders.details')
var arr =
[{
"createdBy":"Tom",
"logistics" : 0,
"paymentStatus" : "Paid",
"orderAmount" : 5393.75,
"details" : {
"street": "S.S road",
"postCode": "440111"
},
"subOrders" : [
{
"name" : "sub Product1",
"mrp" : 12,
"details": "desk"
},
{
"name" : "subProduct2",
"mrp" : 89,
"details": "chair"
}
]
}]
var permissionArray = ['logistics','paymentStatus','details.street', 'details.postCode','subOrders.name', 'subOrders.details'];
var arrFiltered = arr.map(el => {
let newEl = {}
for (let elm of permissionArray){
if (elm.includes('.')){
//console.log(elm);
const split_arr = elm.split('.')
//console.log(el[split_arr[0]]);
if (el[split_arr[0]] instanceof Array){
if (!newEl.hasOwnProperty([split_arr[0]]) ){
newEl[split_arr[0]] = el[split_arr[0]].map((child,index) => ({[split_arr[1]]: child[split_arr[1]]}) )
} else {
el[split_arr[0]].forEach((child,index) => {
//console.log(child[ split_arr[1] ]);
newEl[split_arr[0]][index][split_arr[1]] = child[split_arr[1]];
})
}
} else{
if (!newEl.hasOwnProperty([split_arr[0]]) ){
newEl[split_arr[0]] = {[split_arr[1]]: el[split_arr[0]][split_arr[1]]}
} else {
newEl[split_arr[0]][split_arr[1]] = el[split_arr[0]][split_arr[1]];
}
}
} else {
newEl[elm] = el[elm];
}
}
return newEl;
}
)
console.log(arrFiltered)
.as-console-wrapper { max-height: 100% !important; top: 0; }
NOTE: you need to check first if a dot is included with elm.includes('.'), then you have to split it into its two parts with elm.split('.'), then finally you need to check if the value of the key is an array, like 'subOrders' or if it is just an object like 'details' and handle each case with either directly calling the child key, or mapping over the array and calling the child key.
With the permission array as:
var permissionArray = ['logistics','paymentStatus','details.street', 'details.postCode','subOrders.name', 'subOrders.details'];
The output is:
[
{
"logistics": 0,
"paymentStatus": "Paid",
"details": {
"street": "S.S road",
"postCode": "440111"
},
"subOrders": [
{
"name": "sub Product1",
"details": "desk"
},
{
"name": "subProduct2",
"details": "chair"
}
]
}
]
NOTE: upon request of the OP, this now also allows you to have two subfields of one field like 'subOrders.name', 'subOrders.details'
For this !newEl.hasOwnProperty([split_arr[0]]) is used to check if the property/key doesn't already exist. If it does not, then create it, but if it does, then modify this existing property/key's value/item.
You could take a two step approach where you group same keys in an array and store the nested properties as well to the grouped parents.
This is important if you have same parent keys, like
['subOrders.name', 'subOrders.mrp']
For example a grouped array looks like this:
[
[
"logistics"
],
[
"paymentStatus"
],
[
"details",
["street"]
],
[
"subOrders",
["name", "mrp"]
]
]
Then build new objects based on the key and nested keys.
function filter(keys) {
var pathes = keys.reduce((r, path) => {
var [key, ...rest] = path.split('.'),
temp = r.find(([q]) => q === key);
if (rest.length)
if (temp) temp[1].push(rest.join('.'));
else r.push([key, [rest.join('.')]]);
else
if (!temp) r.push([key]);
return r;
}, []);
return function (object) {
const keys = pathes.filter(([key]) => key in object);
return Object.fromEntries(keys.map(([key, rest]) => [
key,
rest
? Array.isArray(object[key])
? object[key].map(filter(rest))
: filter(rest)(object[key])
: object[key]
]));
};
}
var permissionArray = ['foo', 'logistics', 'paymentStatus', 'details.street', 'subOrders.name'],
data = [{ createdBy: "Tom", logistics: 0, paymentStatus: "Paid", orderAmount: 5393.75, details: { street: "S.S road", postCOde: "440111", }, subOrders: [{ name: "sub Product1", mrp: 12 }, { name: "subProduct2", mrp: 89 }] }],
result = data.map(filter(permissionArray));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Instead of building a filtered output, we may operate on original obj and remove its props.
The permissions represent a tree (or trie if node is a word (. delimited))
If we further assume that any specified path is accessible in data, then any leaf has for parent an array or object (and so forth).
We then build a tree, and for any node traverse data and remove the keys which are not part of our specification.
const data = {"createdBy":"Tom","logistics":0,"paymentStatus":"Paid","orderAmount":5393.75,"details":{"street":"S.S road","postCOde":"440111"},"subOrders":[{"name":"sub Product1","mrp":12},{"name":"subProduct2","mrp":89}],"a":{"b":[{"c":3,"d":"a"},{"c":4,"e":"f"}]}}
const d = JSON.parse(JSON.stringify(data)) // do not mutate original
const v = ['logistics','paymentStatus','details.street','subOrders.name','subOrders.details','a.b.c']
const tree = v.map(x => x.split('.')).reduce((o, p) => {
p.reduce((c, x) => (c[x] = c[x] || {}), o)
return o
},{})
function rec (keep, node) {
if (Object.keys(keep).length === 0) return
if (Array.isArray(node)) { return node.forEach(rec.bind(0, keep)) }
for (const k in node) {
!(k in keep) ? delete node[k] : rec(keep[k], node[k])
}
}
rec(tree, d)
console.log(JSON.stringify(d,null,2))
You can maybe do it like this:
var arr =
[{
"createdBy":"Tom",
"logistics" : 0,
"paymentStatus" : "Paid",
"orderAmount" : 5393.75,
"details" : {
"street": "S.S road",
"postCOde": "440111"
},
"subOrders" : [
{
"name" : "sub Product1",
"mrp" : 12
},
{
"name" : "subProduct2",
"mrp" : 89
}
]
}]
var permissionArray = ['logistics','paymentStatus','details']
var arrFiltered = arr.map(el => {
let newEl = {}
for (let elm of permissionArray) newEl[elm] = el[elm]
return newEl
}
)
console.log(arrFiltered)
In the following solution, we divide the handling of objects and plain arrays.
We build-up a "qualified key" corresponding to the equivalent string in the permissions array, as we recursively descend into an object along its properties.
If we hit a primitive value, then we end the recursion for that property.
A new object sanitized is created for each level of the recursion to contain only the permitted properties.
const data = [{ "createdBy": "Tom", "logistics": 0, "paymentStatus": "Paid", "orderAmount": 5393.75, "details": { "street": "S.S road", "postCOde": "440111" }, "subOrders": [{ "name": "sub Product1", "mrp": 12 }, { "name": "subProduct2", "mrp": 89 }] }]
const isPrimitive = (x)=>x === null || typeof x !== "object"
function sanitize(o, permissions, prefix='') {
if (isPrimitive(o)) return o
const sanitized = {}
for (const[k,v] of Object.entries(o)) {
const qk = `${prefix}${k}`
if (permissions.some((p)=>p.match(new RegExp(`^${qk}`)))) {
sanitized[k] = Array.isArray(v)
? sanitizeArray(v, permissions, `${qk}.`)
: sanitize(v, permissions, `${qk}.`)
}
}
return sanitized
}
const sanitizeArray = (arr,permissions,prefix='')=>arr.map((el)=>sanitize(el, permissions, prefix))
const permissions = ['logistics', 'paymentStatus', 'details.street', 'subOrders.name']
const sanitized = sanitizeArray(data, permissions)
console.log(sanitized)

Calculus with objects in javascript

I have an object that contains 2 fields: day_active and day_inactive. (the object is in the javascript snippet below)
And what I want to obtain is another object that is based on this formula:
count(day_active (on date x))-count(day_inactive (on date x)
{
{
"date" : "2019-09-19",
"type" : "groupC",
"count" : 2.0 // (5.0 - 3.0) - how many groupC were on day_active(2019-09-19) minus how many groupC were on day_inactive(2019-09-19)
},
{
"date" : "2019-09-19",
"type" : "groupW",
"count" : -2.0 // (3.0 - 5.0)
},
{
"date" : "2019-09-11",
"type" : "groupW",
"count" : -2.0 // (8.0 - 10.0)
},
{
"date" : "2019-10-08",
"type" : "groupW",
"count" : 7.0 // (7.0 - 0.0)
}
}
I tried this code but is not taking all the cases and the result is incomplete:
let items = {
"day_inactive" : [
{
"date" : "2019-09-19",
"type" : "groupC",
"count" : 3.0
},
{
"date" : "2019-09-11",
"type" : "groupW",
"count" : 10.0
},
{
"date" : "2019-09-19",
"type" : "groupW",
"count" : 5.0
},
{
"date" : "2019-10-07",
"type" : "groupW",
"count" : 9.0
},
{
"date" : "2019-10-05",
"type" : "groupW",
"count" : 3.0
},
],
"day_active" : [
{
"date" : "2019-09-11",
"type" : "groupW",
"count" : 8.0
},
{
"date" : "2019-09-19",
"type" : "groupW",
"count" : 3.0
},
{
"date" : "2019-10-08",
"type" : "groupW",
"count" : 7.0
},
{
"date" : "2019-09-19",
"type" : "groupC",
"count" : 5.0
}
]
}
let auxObj = {}
for (let i = 0; i < items.day_active.length; i++) {
for (let j = 0; j < items.day_inactive.length; j++) {
if (items.day_active[i].date == items.day_inactive[j].date && items.day_active[i].type == items.day_inactive[j].type) {
// console.log("yes")
auxObj.date = items.day_active[i].date
auxObj.type = items.day_active[i].type
auxObj.count = items.day_active[i].count - items.day_inactive[j].count
}
}
}
console.log(auxObj)
How can I solve this in a simple way? Thank you for your time!
Follow along the comments for explanation...
// let's create an empty object
let output = {};
// and start copying active days...
for (const obj of items.day_active) {
// the following `key` is just for grouping purposes...
const key = `${obj.date}-${obj.type}`;
output[key] = { ...obj };
}
// Now let's look at inactive days...
for (const obj of items.day_inactive) {
// the following `key` is just for grouping purposes...
const key = `${obj.date}-${obj.type}`;
// is this the first time we're looking at this `date-type`? let's add it with 0 count
if (!output[key]) {
output[key] = { ...obj, count: 0 };
}
// and subtract it from active days count
output[key].count -= obj.count;
}
// let's remove the `key` we created earlier...
output = Object.values(output);
// here's the output
console.log(output);
From the sample input given, this is the result we get:
[ { date: '2019-10-11', type: 'groupW', count: -2 },
{ date: '2019-10-19', type: 'groupW', count: 3 },
{ date: '2019-10-08', type: 'groupW', count: 7 },
{ date: '2019-10-19', type: 'groupC', count: 5 },
{ date: '2019-09-19', type: 'groupC', count: -3 },
{ date: '2019-09-19', type: 'groupW', count: -5 },
{ date: '2019-10-07', type: 'groupW', count: -9 },
{ date: '2019-10-05', type: 'groupW', count: -3 } ]
I think this one will be more efficient, i named the object after me cause...idn
let marios = {};
items.day_active.forEach(d => marios[d.date+'_'+d.type] = d.count || 0);
items.day_inactive.forEach(d => marios[d.date+'_'+d.type] = marios[d.date+'_'+d.type] ? marios[d.date+'_'+d.type] - (d.count || 0) : (d.count || 0));
console.log(marios);
The logic behind it is that we create an object and we create a property for each date present in the data sets, starting with the first terms of the formla, ending with the subtraction of the second part, and by defaulting to 0 for each appropriate case.
In the end you can iterate the properties of the object, split the string on '_' to read each propertie's date and group and create an array of the results ( if you have trouble with this let me know )

group array of objects by property first letter

im struggling a little with this, been a while since ive coded javascript ... trying to convert this
items = {
"data": [
{
"name" : "john"
},
{
"name" : "james"
},
{
"name" : "joe"
},
{
"name" : "brian"
},
{
"name" : "bojan"
},
{
"name" : "billy"
},
{
"name" : "dean"
},
{
"name" : "darren"
},
{
"name" : "doug"
}
]
}
into this format
items = {
"data": [
{
letter: "j"
names : ["john", "james", "joe"]
},
{
letter: "b"
names : ["brian", "bojan", "billy"]
},
{
letter: "j"
names : ["dean", "darren", "doug"]
},
]
}
I've been trying to do this using reduce but not having much look.... is there a simpler way to to do it?
You can use reduce to create an object with the letters as keys from which you can extrapolate the array of objects you need by iterating over the object entries using map.
const items = {"data":[{"name":"john"},{"name":"james"},{"name":"joe"},{"name":"brian"},{"name":"bojan"},{"name":"billy"},{"name":"dean"},{"name":"darren"},{"name":"doug"}]};
// `reduce` over the data to produce an object
// with letter keys, and array values where the names are added
const obj = items.data.reduce((acc, c) => {
const letter = c.name[0];
acc[letter] = (acc[letter] || []).concat(c.name);
return acc;
}, {})
// `map` over the object entries to return an array of objects
items.data = Object.entries(obj).map(([letter, names]) => {
return { letter, names }
}).sort((a, b) => a.letter > b.letter);
console.log(items);
Vanilla javascript implementation:
const items = {
"data": [
{
"name" : "john"
},
{
"name" : "james"
},
{
"name" : "joe"
},
{
"name" : "brian"
},
{
"name" : "bojan"
},
{
"name" : "billy"
},
{
"name" : "dean"
},
{
"name" : "darren"
},
{
"name" : "doug"
}
]
}
const transformed = {
data:[]
}
const findByLetter = (letter) => (element) => element.letter === letter;
for(let i = 0; i < items.data.length; i++){
const letter = items.data[i].name.split("")[0];
const elIndex = transformed.data.findIndex(findByLetter(letter));
if(elIndex > -1){
transformed.data[elIndex].names.push(items.data[i].name);
}else{
transformed.data.push({
letter,
names: [items.data[i].name],
});
}
};
console.log(transformed);
Use one reduce():
const items = {"data":[{"name":"john"},{"name":"james"},{"name":"joe"},{"name":"brian"},{"name":"bojan"},{"name":"billy"},{"name":"dean"},{"name":"darren"},{"name":"doug"}]};
let res = items.data.reduce((acc, item) => {
let l = item.name[0];
if (acc.data.filter(e => e.letter == l)[0] === undefined) acc.data.push({'letter': l, names: [] });
acc.data.filter(e => e.letter == l)[0].names.push(item.name);
return acc;
}, {"data": []})
console.log(res)

Javascript - Reduce array with multiple-key objects [duplicate]

This question already has answers here:
Group array items using object
(19 answers)
Closed 6 years ago.
I want to modify a Javascript array, so that the elements having the same values for specifies properties merge into one object in a way that the other properties are kept as a comma-separated string, JSON string, or an array. Basically, I want to turn this:
[
{
"language" : "english",
"type" : "a",
"value" : "value1"
},
{
"language" : "english",
"type" : "a",
"value" : "value2"
},
{
"language" : "english",
"type" : "b",
"value" : "value3"
},
{
"language" : "spanish",
"type" : "a",
"value" : "valor1"
}
]
into this:
[
{
"language" : "english",
"type" : "a",
"value" : ["value1" , "value2"] // A Json string is welcome too
},
{
"language" : "english",
"type" : "b",
"value" : "value3"
},
{
"language" : "spanish",
"type" : "a",
"value" : "valor1"
}
]
I have tried iterating and filtering, then upserted the object as given in the snippet. But I wonder if there is a more elegant way to do that.
P.S EcmaScript6 and additional JS library suggestions are also welcome.
var originalArray = [
{
"language" : "english",
"type" : "a",
"value" : "value1"
},
{
"language" : "english",
"type" : "a",
"value" : "value2"
},
{
"language" : "english",
"type" : "b",
"value" : "value3"
},
{
"language" : "spanish",
"type" : "a",
"value" : "valor1"
}
];
var resultingArray = [];
// iterate through the original array
$.each(originalArray, function(i, val) {
// apply filter on key properties (i.e. language and type)
var result = resultingArray.filter(function( obj ) {
return (obj.language === val.language && obj.type === val.type);
});
// if a record exists, update its value
if (result.length === 1) {
result[0].value += (", " + val.value);
}
// else, add value
else if (result.length === 0) {
resultingArray.push(val);
}
// if multiple rows exist with same key property values...
else {
alert("Too many records with language '" + val.language + "' and type '" + val.type + "'");
}
});
console.log(JSON.stringify(resultingArray));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
This is what you need?
var baseData= [
{
"language" : "english",
"type" : "a",
"value" : "value1"
},
{
"language" : "english",
"type" : "a",
"value" : "value2"
},
{
"language" : "english",
"type" : "b",
"value" : "value3"
},
{
"language" : "spanish",
"type" : "a",
"value" : "valor1"
}
];
var newData = [];
baseData.forEach(function(item, index) {
if (newData.length === 0) {
newData.push(item);
} else {
var dIndex = -1;
newData.forEach(function(itm, idx) {
if (item.language === itm.language && item.type === itm.type) dIndex = idx;
});
if (dIndex !== -1) {
var oldValue = newData[dIndex].value;
if (typeof(oldValue).toString() === 'string') {
newData[dIndex].value = [oldValue, item.value];
}
} else {
newData.push(item);
}
}
});
console.log(newData);

immutablejs & merge with new key

I have the 2 map objects (One is initial state & one from Network ) and I need to merge the intial state with data received from network. However, i keep "hideDetails": true to handle the state in the client side and others from server end.
//initial state
var j = Immutable.fromJS({
"staffs": {
"hasRecievedData": false,
"addingNewStaff": false,
"data": {
"0": {
"name" : null,
"age" : null,
"designation" : null,
"email" : null,
"hideDetails": true
}
}
}
});
// data from network
var m = Immutable.fromJS({
"staffs" : {
"0" : {
"name" : "name1",
"age" : "23",
"designation" : "work1",
"email" : "aliasson#abcd.com"
},
"1" : {
"name" : "name2",
"age" : "22",
"designation" : "work2",
"email" : "aliassson#abcd.com"
}
}
});
I want to merge these two and get the new states as below,
var m = Immutable.fromJS({
"staffs" : {
"hasRecievedData": false,
"addingNewStaff": false,
"0" : {
"name" : "name1",
"age" : "23",
"designation" : "work1",
"email" : "aliasson#abcd.com",
"hideDetails": true
},
"1" : {
"name" : "name2",
"age" : "22",
"designation" : "work2",
"email" : "aliassson#abcd.com",
"hideDetails": true
}
}
});
How can I achieve it using Merge functions?
Update1:
Not sure this is correct or not, however I get the result with this,
f = j.merge({
hasRecievedData: true,
data: m.get("staffs").map(function(x){
return x.set("hideDetails",true);
})
});
From ImmutableJS docs:
var map1 = Immutable.Map({a:1, b:2, c:3, d:4});
var map2 = Immutable.Map({c:10, a:20, t:30});
var obj = {d:100, o:200, g:300};
var map3 = map1.merge(map2, obj);
// Map { a: 20, b: 2, c: 10, d: 100, t: 30, o: 200, g: 300 }

Categories