Concatenate values in same object's property inside reduce - javascript

I have an array of objects like this:
let array = [{
"Age": 20,
"Name": "Kevin"
}, {
"Age": 15,
"Name": "Alfred"
}, {
"Age": 30,
"Name": "Joe"
}];
I want to get an object like this:
{
"Age": '20, 15, 30',
"Name": 'Kevin, Alfred, Joe'
}
If I do:
let r = array.reduce(function(pV, cV) {
Object.keys(cV).map(function(key){
pV[key] = (pV[key] || []).concat(cV[key]);
});
return pV;
},{});
console.log(r); // { "Age": [20, 15, 30], "Name": ['Kevin', 'Alfred', 'Joe'] }
Or if I do:
let r = array.reduce(function(pV, cV) {
Object.keys(cV).map(function(key){
pV[key] = (pV[key] || '') + ', ' + cV[key];
});
return pV;
},{});
console.log(r); // { "Age": ', 20, 15, 30', "Name": ', Kevin, Alfred, Joe' }
I'm a little bit lost. Some ideas on how to get what I need?

You are quite close with your second appraoch, you just have to make sure that the initial , does not appear, which is quite easy with a ternary:
let r = array.reduce(function(pV, cV) {
Object.keys(cV).map(function(key){
pV[key] = (pV[key] ? (pV[key] + ", ") : '') + cV[key];
});
return pV;
},{});

You can try following code using .reduce() and Object.keys
let array = [{
"Age": 20,
"Name": "Kevin"
}, {
"Age": 15,
"Name": "Alfred"
}, {
"Age": 30,
"Name": "Joe"
}];
let result = array.reduce((current,result) => {
Object.keys(current).forEach(key => {
if(!result[key]){
result[key] = current[key];
} else {
result[key]+= ", " + current[key];
}
})
return result;
},{});
console.log(result);

I wouldn't use reduce like that, which is just a glorified loop:
let r = {};
for (const cV of array) {
for (const key in cV) {
r[key] = (r[key] || []).concat(cV[key]);
}
}
For a functional approach, where the map would be actually useful, I'd nest the iterations the other way round:
let r = {};
for (const key of ["Age", "Name"]) { // or Object.keys(array[0])
r[key] = array.map(function(cV){
return cV[key];
}).join(", ");
}

You can do in this way as well.
array.reduce((a,n)=>{
a.Age = a.Age+','+n.Age;
a.Name = a.Name+','+n.Name;
return a;
});
Warning!! this approach with modify actual array of object.
If that is not intended then you can clone object first and then do reduce.
I did JSON.parse(JSON.stringify(array)) to clone array you can use your own ways to deep clone it.
JSON.parse(JSON.stringify(array)).reduce((a,n)=>{
a.Age = a.Age+', '+n.Age;
a.Name = a.Name+', '+n.Name;
return a;
})

Related

How do I create new object after manipulation

I have a JavaScript object like the following below availability and reserved, here I need to subtract quantity value from availability.
var availability = {"bike":10,"cycle":3,"car":1};
var reserved ={"cycle":1,"bike":10}
how should I get this response as below?
response = {"bike":0,"cycle":2,"car":1};
Why not a simple for loop.
var availability = { bike: 10, cycle: 3, car: 1 };
var reserved = { cycle: 1, bike: 10 };
let response = {};
for (let key in availability) {
if (reserved[key]) {
response[key] = availability[key] - reserved[key];
} else {
response[key] = availability[key];
}
}
console.log(response);
Output:
{ bike: 0, cycle: 2, car: 1 }
There are many way to solve this, but I recommend using reduce().
var availibilty = {
"bike": 10,
"cycle": 3,
"car": 1
};
var reserved = {
"cycle": 1,
"bike": 10
}
function calc(a, b) {
const answer = Object.keys(a).reduce((acc, key) => {
return {
...acc,
[key]: a[key] - (b[key] || 0)
}
}, {});
console.log(answer);
}
calc(availibilty, reserved);
You can iterate through each key-value pair and subtract quantity in availability with the corresponding key in reserved. Then create your result object using Object.fromEntries().
const availability = { "bike" : 10, "cycle" : 3, "car" : 1 },
reserved ={ "cycle": 1,"bike": 10 },
result = Object.fromEntries(Object.entries(availability).map(([key, value]) => [key, value - (reserved[key] ?? 0)]));
console.log(result);
You can loop through the Object. keys() of one object you provided and subtract the other using reduce() method.
var availibilty = {"bike":10,"cycle":3,"car":1};
var reserved ={"cycle":1,"bike":10}
let response = Object.keys(availibilty).reduce((x, y) => {
x[k] = availibilty[y] - reserved[y];
return x;
}, {});
console.log(response);
Please find Array.reduce implementation.
Logic
Loop through keys of availability object.
Find the values of each keys from reserved object.
Store the difference as the value for same key in the accumulator array.
var availability = { "bike": 10, "cycle": 3, "car": 1 };
var reserved = { "cycle": 1, "bike": 10 };
const response = Object.keys(availability).reduce((acc, curr) => {
acc[curr] = availability[curr] - (reserved[curr] || 0);
return acc;
}, {});
console.log(response);

Convert an array to json object by javascript

I am stuck to solve this problem.
Convert an array below
var input = [
'animal/mammal/dog',
'animal/mammal/cat/tiger',
'animal/mammal/cat/lion',
'animal/mammal/elephant',
'animal/reptile',
'plant/sunflower'
]
to json Object
var expectedResult = {
"animal": {
"mammal": {
"dog": true,
"cat": {
"tiger": true,
"lion": true
},
"elephant": true
},
"reptile": true
},
"plant": {
"sunflower": true
}
}
Which data structure and algorithm can I apply for it?
Thanks
You need to first split each element to convert to array
using reverse reduce method you can convert them to object.
And your last step is merge this objects.
Lodash.js merge method is an one way to merge them.
var input = ['animal/mammal/dog','animal/mammal/cat/tiger','animal/mammal/cat/lion', 'animal/mammal/elephant','animal/reptile', 'plant/sunflower']
var finalbyLodash={}
input.forEach(x=>{
const keys = x.split("/");
const result = keys.reverse().reduce((res, key) => ({[key]: res}), true);
finalbyLodash = _.merge({}, finalbyLodash, result);
});
console.log(finalbyLodash);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.js"></script>
To make the process more understandable, break the problem down into pieces.
The first step is convert each string into something we can use, converting this:
"animal/mammal/dog"
into this:
[ "animal", "mammal", "dog" ]
That's an array of property names needed to build the final object.
Two functions will accomplish this for you, String.prototype.split() to split the string into an array, and Array.prototype.map() to transform each of the array elements:
let splitIntoNames = input.map(str => str.split('/'));
The intermediate result is this:
[
[ "animal", "mammal", "dog" ],
[ "animal", "mammal", "cat", "tiger" ],
[ "animal", "mammal", "cat", "lion" ],
[ "animal", "mammal", "elephant" ],
[ "animal", "reptile" ],
[ "plant", "sunflower" ]
]
Next step is to iterate over each array, using Array.prototype.forEach() to add properties to the object. You could add properties to the object with a for loop, but let's do that with a recursive function addName():
function addName(element, list, index) {
if (index >= list.length) {
return;
}
let name = list[index];
let isEndOfList = index === list.length - 1;
element[name] = element[name] || (isEndOfList ? true : {});
addName(element[name], list, index + 1);
}
let result = {};
splitIntoNames.forEach((list) => {
addName(result, list, 0);
});
The result:
result: {
"animal": {
"mammal": {
"dog": true,
"cat": {
"tiger": true,
"lion": true
},
"elephant": true
},
"reptile": true
},
"plant": {
"sunflower": true
}
}
const input = [
"animal/mammal/dog",
"animal/mammal/cat/tiger",
"animal/mammal/cat/lion",
"animal/mammal/elephant",
"animal/reptile",
"plant/sunflower",
];
let splitIntoNames = input.map((str) => str.split("/"));
console.log("splitIntoNames:", JSON.stringify(splitIntoNames, null, 2));
function addName(element, list, index) {
if (index >= list.length) {
return;
}
let name = list[index];
let isEndOfList = index === list.length - 1;
element[name] = element[name] || (isEndOfList ? true : {});
addName(element[name], list, index + 1);
}
let result = {};
splitIntoNames.forEach((list) => {
addName(result, list, 0);
});
console.log("result:", JSON.stringify(result, null, 2));
You can create a function that will slice every element from the array by "/" than you put the results into a variable and than just mount the Json. I mean something like that below:
window.onload = function() {
var expectedResult;
var input = [
'animal/mammal/dog',
'animal/mammal/cat/tiger',
'animal/mammal/cat/lion',
'animal/mammal/elephant',
'animal/reptile',
'plant/sunflower'
]
input.forEach(element => {
var data = element.split('/');
var dog = data[2] === 'dog' ? true : false
var tiger = data[2] === 'cat' && data[3] === 'tiger' ? true : false
var lion = data[2] === 'cat' && data[3] === 'lion' ? true : false
expectedResult = {
data[0]: {
data[1]: {
"dog": dog,
"cat": {
"tiger": tiger,
"lion": lion
}
}
}
}
})
}
Late to the party, here is my try. I'm implmenting recursive approach:
var input = ['animal/mammal/dog', 'animal/mammal/cat/tiger', 'animal/mammal/cat/lion', 'animal/mammal/elephant', 'animal/reptile', 'plant/sunflower'];
result = (buildObj = (array, Obj = {}) => {
array.forEach((val) => {
keys = val.split('/');
(nestedFn = (object) => {
outKey = keys.shift();
object[outKey] = object[outKey] || {};
if (keys.length == 0) object[outKey] = true;
if (keys.length > 0) nestedFn(object[outKey]);
})(Obj)
})
return Obj;
})(input);
console.log(result);
I try with array reduce, hope it help
let input = [
"animal/mammal/dog",
"animal/mammal/cat/tiger",
"animal/mammal/cat/lion",
"animal/elephant",
"animal/reptile",
"plant/sunflower",
];
let convertInput = (i = []) =>
i.reduce((prev, currItem = "") => {
let pointer = prev;
currItem.split("/").reduce((prevPre, currPre, preIdx, arrPre) => {
if (!pointer[currPre]) {
pointer[currPre] = preIdx === arrPre.length - 1 ? true : {};
}
pointer = pointer[currPre];
}, {});
return prev;
}, {});
console.log(JSON.stringify(convertInput(input), null, 4));

How to calculate the sum of items in an array?

Suppose i have an array:
const items = [
{
"amount1": "100",
"amount2": "50",
"name": "ruud"
},
{
"amount1": "40",
"amount2": "60",
"name": "ted"
}
]
I want to get all amount1 and amount2 props totalled and result in:
[
{
"amount1": 140,
"amount2": 110
}
]
How can I do this?
Using Array.prototype.reduce() with Object.entries() and Array.prototype.forEach():
const items = [{amount1: 100, amount2: 50}, {amount1: 40, amount2: 60}];
const sums = items.reduce((acc, item) => {
Object.entries(item).forEach(([k, v]) => acc[k] = (acc[k] || 0) + v);
return acc;
}, {});
console.log(sums);
To filter out non-number properties (but keep quoted number strings, as per the updated question):
const items = [{amount1: '100', amount2: '50', name: 'Ruud'}, {amount1: '40', amount2: '60', name: 'Ted'}];
const sums = items.reduce((acc, item) => {
Object.entries(item)
.filter(([_, v]) => !isNaN(v))
.forEach(([k, v]) => acc[k] = (acc[k] || 0) + Number(v));
return acc;
}, {});
console.log(sums);
const items = [{amount1: 100, amount2: 50}, {amount1: 40, amount2: 60}];
function sum(data){
const keys = Object.keys(data[0])
let res = {}
for(key of keys)
res[key]=data.map(x=>x[key]).reduce((a,b)=>a+b);
return res
}
console.log(sum(items))
Here is an alternative, simple, and clean solution for this.
const items = [{amount1:100, amount2:50, name:"ruud"},{amount1:40,amount2:60,name:"ted"}]
let result = [{amount1:0,amount2:0}]
items.forEach(i=>{
result[0].amount1 += i.amount1
result[0].amount2 += i.amount2
})
console.log(result)
Above solutions are great. I included this if you don't want to use
Array.prototype.reduce(). This will work even if you have other properties which are not "numbers"
const items = [{amount1: 100, amount2: 50, name: 'Ruud'}, {amount1: 40, amount2: 60, name: 'Ted'}];
var result = {};
items.forEach(function(eachItem){
for(var prop in eachItem){
if(typeof eachItem[prop] === "number"){
result[prop] = result[prop] ? result[prop] + eachItem[prop] : eachItem[prop];
}
}
});
result = [result];
console.log(result);
You can use reduce().
Use the reduce() method on the array items
Set the accumulator(ac) as an empty object i.e {}
During each iteration through the objects create a for..in loop to iterate through all keys of object.
Check if the typeof value of key is "number" then add it otherwise don't
const items = [{amount1:100, amount2:50, name:"ruud"}, {amount1:40,amount2:60,name:"ted"}]
let res = [items.reduce((ac,x) => {
for(let key in x){
if(typeof x[key] === "number"){
if(!ac[key]) ac[key] = 0;
ac[key] += x[key]
}
}
return ac;
},{})]
console.log(res)
reduce() is indeed the way to go, but the cleanest to go only through a set of known keys is probably to pass your expected result as the accumulator and to iterate over this accumulator's keys:
const items = [
{ amount1: "100", amount2: "50", name: "ruud", foo: "unrelated" },
{ amount1: "40", amount2: "60", name: "ted", foo: "0" }
];
const result = items.reduce((acc, item) => {
for (let key in acc) { // iterate over the accumulator's keys
acc[key] += isNaN(item[key]) ? 0 : +item[key];
}
return acc;
}, { // here we define the expected format
amount1: 0,
amount2: 0
});
console.log(result);

add repeating strings value in json array

I have JSON as
var newJSON = [{
"key": "India",
"value": "72"
}, {
"key": "India",
"value": "27"
}, {
"key": "Pakistan",
"value": "90"
}, {
"key": "Zimbamwe",
"value": "88"
}, {
"key": "India",
"value": "100"
}, {
"key": "Pakistan",
"value": "172"
}]
I want desired result as below, where the duplicate key values have their value properties added together:
[{
"key": "India",
"value": "199"
}, {
"key": "Pakistan",
"value": "262"
}, {
"key": "Zimbamwe",
"value": "88"
}]
Please help me with this
Here is the solution:
var grouped = [];
var added = [];
for(var i = 0; i < json.length; i++) {
var indexOfCountry = added.indexOf(json[i].key);
if (indexOfCountry >= 0)
{
grouped[indexOfCountry].value = (Number(grouped[indexOfCountry].value) + Number(json[i].value)).toString();
}
else {
grouped.push(json[i]);
added.push(json[i].key);
}
}
grouped array is your desired result.
Fiddle: https://jsfiddle.net/zummw9zp/
Yet another variant with reduce
var result = newJSON.reduce(function(acc, el){
var val = acc.map[el.key];
if(!val){
acc.map[el.key] = val = { key:el.key, value: parseInt(el.value) };
acc.result.push(val);
}else{
val.value += parseInt(el.value);
}
return acc;
},{map:{}, result:[]}).result;
var newJSON = [
{"key":"India","value":"72"},{"key":"India","value":"27"},
{"key":"Pakistan","value":"90"},{"key":"Zimbamwe","value":"88"},
{"key":"India","value":"100"},{"key":"Pakistan","value":"172"}
];
document.getElementById('r').innerHTML = 'newJSON: ' + JSON.stringify(newJSON);
var result = newJSON.reduce(function(acc, el){
var val = acc.map[el.key];
if(!val){
acc.map[el.key] = val = { key:el.key, value: parseInt(el.value) };
acc.result.push(val);
}else{
val.value += parseInt(el.value);
}
return acc;
},{map:{}, result:[]}).result;
document.getElementById('r').innerHTML += '<br /><br />result: ' + JSON.stringify(result);
<div id="r"></div>
This is a classic use case for reduce, which is designed to take arrays and somehow, well, "reduce" them to other things, by looping across them and transforming the result at each iteration.
return newJSON.reduce(function(result, entry) {
key = entry.key;
result[key] = result[key] || { key: key, value: 0 };
result[key].value += entry.value;
return result;
}, {});
Using Underscore
If you're OK with using a library like Underscore, you can write this as
_.mapObject(_.groupBy(newJSON, 'key'), total)
Using a narrative style where we describe
`A(B, C)`
as
Take B and do A to it usingC
and
`A(B(C))`
as
Take C and do B to it. Then take the result and do A to it
we can almost read this as English:
Take newJSON and group it by using key. Then take the result and map the object using total
_.groupBy produces an object keyed by some property and returns groups, which are arrays of all the entries falling into each group:
{
India: [ {key: "India", value: 72}, {key: "India", value: 100... ],
...
}
total calculates the total value for each group:
function total(group) { return sum(group . map(value)); }
So it converts an array of entries
[ {key: "India", value: 72}, {key: "India", value: 100}, ... ],
into 199. We use this to map the arrays of entries to total scores using _.mapObject.
Here sum can be written as
function sum(array) { return array.reduce(add); }
function add(a, b) { return a+b; }
and value is a little utility function to retrieve the value property:
function value(entry) { return entry.value; }
So the complete solution is:
function add (a, b) { return a+b; }
function sum (array) { return array.reduce(add); }
function value (entry) { return entry.value; }
function total (group) { return sum(group . map(value)); }
_.mapObject(_.groupBy(newJSON, 'key'), total)

Remove duplicate objects from JSON file in JavaScript

This is the current JSON file:
[{
"name": "Peter",
"age": 30,
"hair color": "brown"
}, {
"name": "Steve",
"age": 55,
"hair color": "blonde"
}, {
"name": "Steve",
"age": 55,
"hair color": "blonde"
}]
I want to remove the duplicate Steve individual from the list. How can I make a new JSON that checks if the object's name matches and remove any duplicates in JavaScript?
You must load the JSON data in to the program and parse that with JSON.parse, like this
var array = JSON.parse(content.toString())
To filter out the repeated names from the array of Objects, we use Array.prototype.filter function. You can store the names in an object, and next time when the same name appears we simply filter it out from the result.
var seenNames = {};
array = array.filter(function(currentObject) {
if (currentObject.name in seenNames) {
return false;
} else {
seenNames[currentObject.name] = true;
return true;
}
});
console.log(array);
# [ { name: 'Peter', age: 30, 'hair color': 'brown' },
# { name: 'Steve', age: 55, 'hair color': 'blonde' } ]
var data = [{
"name": "Peter",
"age": 30,
"hair color": "brown"
}, {
"name": "Steve",
"age": 55,
"hair color": "blonde"
}, {
"name": "Steve",
"age": 55,
"hair color": "blonde"
}]
data = this.data.filter((obj, pos, arr) => {
return arr.map(mapObj =>
mapObj.name).indexOf(obj.name) == pos;
});
console.log(data);
Using Underscore.js and the uniq function:
_.uniq(array, false, function (item) { return item.name; })
Loop, check, splice, repeat:
var distinctValues = {};
for (var i = 0; i < data.length; i++) {
if (distinctValues.hasOwnProperty(data[i].name]) {
//already has it
data.splice(i, 1);
i--;
} else {
distinctValues[data[i].name] = true;
}
}
This was the best solution I could find that makes you able to filter on multiple values in you json object. Solution without _ (module)
array.filter((thing, index, self) =>
index === self.findIndex((t) => (
t.place === thing.place && t.name === thing.name // you can add more arguments here to filter more
))
)
//Other example
array.filter((thing, index, self) =>
index === self.findIndex((t) => (
t.place === thing.place && t.name === thing.name && t.time === thing.time
))
)
Python one-liner from CLI:
cat file_with_duplicates.json | python2 -c 'import sys; import json; sys.stdout.write(json.dumps(reduce(lambda x, y: x + [y] if y not in x else x, json.loads(sys.stdin.read()), [])))' > unique_items.txt

Categories

Resources