Javascript: map an array of items and call function for data - javascript

I am new to javascript. I have an id, name and time that I am trying to get from my data and for each name I am trying to loop through the data and call a function from each name. Any help would be much appreciated. Thank you so much!
This is what I have done
const data = [
[
{
"id": "14hyzdrdsquo",
"name": "Ronald",
"time": '12pm',
},
],
[
{
"id": "1f496w43b8yi",
"name": "Jack",
"time": '1am',
},
],
]
const getData = (id, name, time) => {
const ids = [] // desired ['14hyzdrdsquo','1f496w43b8yi']
const names = []// desired ['Ronald','Jack']
const times = []// desired ['12pm','1am']
ids.push(id) // should have each id in this array
names.push(name) // should have each name in this array
times.push(time) // should have each time in this array
}
var id = Math.random().toString(16).slice(2)
data.map(j => j.map(i => getData(id, i.name, i.time)))

Using a for...of loop, you can loop through your array and group on the keys of each object. Since you have arrays in your outer array, you can use another for loop to loop over those and get each object. Then you can use a for...in loop to over the keys in your object. For each key, you can check if it exists within grouped, and if it does, concatenate the object's value to the grouped array. If it doesn't exist, you can create a new element, and push the value. Once you have grouped everything, you can use destructuring to pull out the array values from your object into variables:
const data = [[{ "id": "14hyzdrdsquo", "name": "Ronald", "time": '12pm', }, ], [{ "id": "1f496w43b8yi", "name": "Jack", "time": '1am', }, ],];
const grouped = {};
for(const arr of data) {
for(const obj of arr) {
for(const key in obj) {
grouped[key] = (grouped[key] || []).concat(obj[key]);
}
}
}
const {id, name, time} = grouped;
console.log(id);
console.log(name);
console.log(time);
The above concept can be achieved with .reduce() as well, where the grouped array gets built by using the accumulator argument of the reduce method, and each object is iterated using Object.entries() with .forEach():
const data = [[{ "id": "14hyzdrdsquo", "name": "Ronald", "time": '12pm', }, ], [{ "id": "1f496w43b8yi", "name": "Jack", "time": '1am', }, ],];
const grouped = data.reduce((acc, arr) => {
arr.forEach(obj => {
Object.entries(obj).forEach(([key, val]) => {
acc[key] = [...(acc[key] || []), val];
});
})
return acc;
}, {});
const {id, name, time} = grouped;
console.log(id);
console.log(name);
console.log(time);
Lastly, if you're happy with doing multiple iterations through your array, you can use .flatMap() to iterate through your array, and for each array, .map() over the objects inside of that. For each object you can extract the key using destructuring assignment. The result of the inner map is then flattened into the outer resulting array due to .flatMap():
const data = [[{ "id": "14hyzdrdsquo", "name": "Ronald", "time": '12pm', }, ], [{ "id": "1f496w43b8yi", "name": "Jack", "time": '1am', }, ],];
const id = data.flatMap(arr => arr.map(({id}) => id));
const name = data.flatMap(arr => arr.map(({name}) => name));
const time = data.flatMap(arr => arr.map(({time}) => time));
console.log(id);
console.log(name);
console.log(time);

You could destructure the double nested array and take the entries of the object and push the values to the same named properties with 's'.
Ath the end destructure the objct for single arrays.
const
data = [[{ id: "14hyzdrdsquo", name: "Ronald", time: '12pm' }], [{ id: "1f496w43b8yi", name: "Jack", time: '1am' }]],
{ ids, names, times } = data.reduce((r, [o]) => {
Object.entries(o).forEach(([k, v]) => (r[k + 's'] ??= []).push(v));
return r;
}, {});
console.log(ids);
console.log(names);
console.log(times);

The problems is you put
const ids = [] // desired ['14hyzdrdsquo','1f496w43b8yi']
const names = []// desired ['Ronald','Jack']
const times = []// desired ['12pm','1am']
inside getData function. That's mean you created new one arrays time when call getData
and for you solution better use .forEach instead of .map.
const data = [
[
{
"id": "14hyzdrdsquo",
"name": "Ronald",
"time": '12pm',
},
],
[
{
"id": "1f496w43b8yi",
"name": "Jack",
"time": '1am',
},
],
]
const ids = [] // desired ['14hyzdrdsquo','1f496w43b8yi']
const names = []// desired ['Ronald','Jack']
const times = []// desired ['12pm','1am']
const getData = (id, name, time) => {
ids.push(id) // should have each id in this array
names.push(name) // should have each name in this array
times.push(time) // should have each time in this array
}
var id = Math.random().toString(16).slice(2)
data.forEach(j => j.forEach(i => getData(id, i.name, i.time)))
console.log(ids)
console.log(names)
console.log(times)

You may do that using reduce inside each loop so you can have lower complexity.
const data = [
[
{
id: '14hyzdrdsquo',
name: 'Ronald',
time: '12pm',
},
{
id: '14hyzdrdsquo',
name: 'Ronald',
time: '12pm',
},
],
[
{
id: '1f496w43b8yi',
name: 'Jack',
time: '1am',
},
{
id: '1f496w43b8yi',
name: 'Jack',
time: '1am',
},
],
];
let ids = [];
let names = [];
let times = [];
data.forEach((elem) => {
const obj = elem.reduce(
(acc, curr, i) => {
acc.ids.push(curr.id);
acc.names.push(curr.name);
acc.times.push(curr.time);
return acc;
},
{
ids: [],
names: [],
times: [],
}
);
ids = [...ids, ...obj.ids];
names = [...names, ...obj.names];
times = [...times, ...obj.times];
});
console.log({
ids,
names,
times,
});

Related

Array of object into a nested object for every value in the array

Trying to turn an array of objects into a nested object. Is there a good method for this? and how do I make it depending on the array length?
Working but is not universal:
https://codesandbox.io/s/thirsty-roentgen-3mdcjv?file=/src/App.js
What I have:
sorting: [
{
"id": "HighestDegree",
"options": [
"HighSchool",
"Undergraduate",
"Bachelor",
"Master",
"Doctor"
]
},
{
"id": "gender",
"options": [
"male",
"female"
]
}
]
What I want:
value: {
"Region": "Oklahoma",
"HighestDegree": {
"HighSchool": {
"male": null,
"female":null
},
"Undergraduate":{
"male": null,
"female":null
}
//and so on...
}
}
The code beneath works but is hardcoded for only two different options. I want it to be able to nest the length of the array. So lets say another object was age it would be {"HighSchool":{male:{"<25":null,"25-35":null}}} etc..
function testSortingArray() {
let sorting = [
{
id: "HighestDegree",
options: ["HighSchool", "Undergraduate", "Bachelor", "Master", "Doctor"]
},
{
id: "gender",
options: ["male", "female"]
}
];
let GoalArray = {};
if (sorting.length > 0) {
sorting[0].options.map((firstArray) => {
let currObject = {};
sorting[1].options.map((secondOption) => {
currObject[secondOption] = null;
});
GoalArray[firstArray] = currObject;
});
}
return GoalArray;
}
console.log(testSortingArray());
You can do it with a recursive function.
The function below reduces every options array to an object, and then continues populating that object if there are rest elements left from the original sorting array.
const fn = ([{ options }, ...rest]) => options.reduce((a, v) => ({
...a,
[v]: rest.length ? fn(rest): null
}), {});
const result = fn(sorting);
Besides the reduce() method, the code above makes use of object and array destructuring and spread syntax.
Complete snippet:
const sorting = [{
"id": "HighestDegree",
"options": [
"HighSchool",
"Undergraduate",
"Bachelor",
"Master",
"Doctor"
]
}, {
"id": "gender",
"options": [
"male",
"female"
]
}, {
"id": "age",
"options": [
"<25",
"25-35"
]
}];
const fn = ([{ options }, ...rest]) => options.reduce((a, v) => ({
...a,
[v]: rest.length ? fn(rest): null
}), {});
const result = fn(sorting);
console.log(result);

Assign to object within map

I'd like to assign to an object within an array map
Heres the array of objects I want to add to
const arr = [
{
"key": "Mike",
"ref": 11800
},
{
"key": "Raph",
"ref": 9339
},
{
"key": "Leo",
"ref": 2560
},
]
I want to add add a new property to the object called slug while I loop over it like below. Possibly map is not the right function to use here because ESLINT complains about assigning within the map.
arr.map((item) => {
...item,
item.slug = `${item.key.toLowerCase();}/${String(item.ref)}`
});
.map() returns a new array containing the results of calling provided function for each element, so you should assign it to the new variable:
const arr = [{
"key": "Mike",
"ref": 11800
},
{
"key": "Raph",
"ref": 9339
},
{
"key": "Leo",
"ref": 2560
},
]
const newArr = arr.map(item => ({
...item,
slug: `${item.key.toLowerCase()}/${String(item.ref)}`
}))
console.dir(newArr)
If you want to add something to existing objects within an array you should use a for loop or .forEach():
const arr = [{
"key": "Mike",
"ref": 11800
},
{
"key": "Raph",
"ref": 9339
},
{
"key": "Leo",
"ref": 2560
},
]
arr.forEach(item => {
item.slug = `${item.key.toLowerCase()}/${String(item.ref)}`
})
console.dir(arr)
When mutating an array, or perform operations with side-effects, you should use a for loop or the Array.prototype.forEach method. If you want to perform pure functional operations over an array, then use Array.prototype.filter, Array.prototype.map, etc.
If you want to set a new property on the existing array elements then do this:
const arr = [ { key: "Mike", ref: 11800 }, /*etc*/ ];
for( const e of arr ) {
e.slug = e.key.toLowerCase() + "/" + e.ref.toString();
}
If you want to generate a new array with new members, then do this:
const arr = [ { key: "Mike", ref: 11800 }, /*etc*/ ];
// Note the parentheses within `map` to avoid ambiguous syntax:
const newArr = arr.map( e => ( { slug: e.key.toLowerCase() + "/" + e.ref.toString() } ) );
console.log( newArr ); // [ { slug: "mike/11800" } ]
Alternatively, to copy over all properties and then add new properties use Object.assign:
const arr = [ { key: "Mike", ref: 11800 }, /*etc*/ ];
const newArr = arr.map( e => Object.assign( {}, e, { slug: e.key.toLowerCase() + "/" + e.ref.toString() } ) );
console.log( newArr ); // [ { key: "Mike", ref: 11800, slug: "mike/11800" } ]

Convert an array of objects to a dictionary by letter [duplicate]

This question already has answers here:
Most efficient method to groupby on an array of objects
(58 answers)
Closed 3 years ago.
I have an array of objects that looks like this:
let stuff = [
{
"id": "48202847",
"name": "Doe"
},
{
"id": "17508",
"name": "Marie"
},
{
"id": "175796",
"name": "Robert"
},
{
"id": "175796",
"name": "Ronald"
},
]
What I want to get is a dictionary looking something like this:
{
"D": [{"id": "48202847", "name": "Doe"}],
"M": [{"id": "17508", "name": "Marie"}],
"R": [{"id": "175796", "name": "Robert"}, {"id": "175796", "name": "Ronald"}]
}
Notice how all the people whose name starts with "R" are listed under one key.
This is my function that creates a dictionary with the person's name as the key:
const byId = (array) =>
array.reduce((obj, item) => {
obj[item.name] = item
return obj
}, {})
But this obviously doesn't do what I want it to. I do have some ideas of how to make this possible, but they are extremely legacy and I would love to know how to do this right.
Any help is appreciated!
You need the first character, uppercase and an array for collecting the objects.
const byId = array =>
array.reduce((obj, item) => {
var key = item.name[0].toUpperCase(); // take first character, uppercase
obj[key] = obj[key] || []; // create array if not exists
obj[key].push(item); // push item
return obj
}, {});
let stuff = [{ id: "48202847", name: "Doe" }, { id: "17508", name: "Marie" }, { id: "175796", name: "Robert" }, { id: "175796", name: "Ronald" }],
result = byId(stuff)
console.log(result);
Here's a solution based on Set, map, reduce and filter:
let stuff = [{"id": "48202847","name": "Doe"},{"id": "17508","name": "Marie"},{"id": "175796","name": "Robert"},{"id": "175796","name": "Ronald"}];
let result = [...new Set(stuff.map(x => x.name[0]))]
.reduce((acc, val) => {
return acc = { ...acc,
[val]: stuff.filter(x => x.name.startsWith(val))
}
}, {});
console.log(result);
Great solution Nina! Could be made a little cleaner by utilizing the spread operator.
const byId = (array) =>
array.reduce((obj, item) => {
var key = item.name[0].toUpperCase();
return {
...obj,
[key]: obj[key] ? [...obj[key], item] : [item],
}
}, {});

How would I write this reportCandidates' function better?

This is the input data in array named candidatesArray:
[
{"name":"george","languages":["php","javascript","java"],"age":19,"graduate_date":1044064800000,"phone":"32-991-511"},
{"name":"anna","languages":["java","javascript"],"age":23,"graduate_date":1391220000000,"phone":"32-991-512"},
{"name":"hailee","languages":["regex","javascript","perl","go","java"],"age":31,"graduate_date":1296525600000,"phone":"32-991-513"}
]
I need to transform in this collection as a result of the function:
{candidates: [
{name: "George", age: 19, phone: "32-991-511"},
{name: "Hailee", age: 31, phone: "32-991-513"},
{name: "Anna", age: 23, phone: "32-991-512"}
],
languages: [
{lang:"javascript",count:1},
{lang:"java", count:2},
{lang:"php", count:2},
{lang:"regex", count:1}
]}
The function repCandidates:
const reportCandidates = (candidatesArray) => {
return repObject}
I need to write it in javascript ES6
I shouldn't use loops(for, while, repeat) but foreach is allowed and it could be better if I use "reduce" function
The candidates should be return by their name, age and phone organized by their graduate_date.
The languages should be returned with their counter in alphabetic order .
Visit https://codepen.io/rillervincci/pen/NEyMoV?editors=0010 to see my code, please.
One option would be to first reduce into the candidates subobject, while pushing the langauges of each to an array.
After iterating, sort the candidates and remove the graduate_date property from each candidate, then use reduce again to transform the languages array into one indexed by language, incrementing the count property each time:
const input = [{
"name": "george",
"languages": ["php", "javascript", "java"],
"age": 19,
"graduate_date": 1044064800000,
"phone": "32-991-511"
}, {
"name": "anna",
"languages": ["java", "javascript"],
"age": 23,
"graduate_date": 1391220000000,
"phone": "32-991-512"
}, {
"name": "hailee",
"languages": ["regex", "javascript", "perl", "go", "java"],
"age": 31,
"graduate_date": 1296525600000,
"phone": "32-991-513"
}];
const output = input.reduce((a, { languages, ...rest }) => {
a.candidates.push(rest);
a.languages.push(...languages);
return a;
}, { candidates: [], languages: [] });
output.candidates.sort((a, b) => a.graduate_date - b.graduate_date);
output.candidates.forEach(candidate => delete candidate.graduate_date);
output.languages = Object.values(
output.languages.reduce((a, lang) => {
if (!a[lang]) a[lang] = { lang, count: 0 };
a[lang].count++;
return a;
}, {})
);
output.languages.sort((a, b) => a.lang.localeCompare(b.lang));
console.log(output);
It's common practice to do everything in a reduce(), but sometimes it's easier to read if you break it up a bit. This creates a counter object as a helper to to track the language counts. map()s over the array to pull out the languages and personal info and then puts it all together:
let arr = [ {"name":"george","languages":["php","javascript","java"],"age":19,"graduate_date":1044064800000,"phone":"32-991-511"},{"name":"anna","languages":["java","javascript"],"age":23,"graduate_date":1391220000000,"phone":"32-991-512"},{"name":"hailee","languages":["regex","javascript","perl","go","java"],"age":31,"graduate_date":1296525600000,"phone":"32-991-513"}]
let lang_counter = {
// helper keeps counts of unique items
counts:{},
add(arr){
arr.forEach(item => this.counts[item] = this.counts[item] ? this.counts[item] + 1 : 1)
},
toarray(){
return Object.entries(this.counts).map(([key, val]) => ({[key]: val}))
}
}
// iterate over object to create candidates
let candidates = arr.map(row => {
let {languages, ...person} = row
lang_counter.add(languages) // side effect
return person
})
// put them together
console.log({candidates, languages:lang_counter.toarray()})
You can use Array.reduce and Object.values like below
let arr = [{"name":"george","languages":["php","javascript","java"],"age":19,"graduate_date":1044064800000,"phone":"32-991-511"},{"name":"anna","languages":["java","javascript"],"age":23,"graduate_date":1391220000000,"phone":"32-991-512"},{"name":"hailee","languages":["regex","javascript","perl","go","java"],"age":31,"graduate_date":1296525600000,"phone":"32-991-513"}]
let res = arr.reduce((o, {name, age, phone, graduate_date, languages}) => {
o.candidates.push({name, age, phone, graduate_date})
languages.forEach(l => {
o.languages[l] = o.languages[l] || { lang:l, count: 0 }
o.languages[l].count++
})
return o
}
, { candidates: [], languages: {}})
res.candidates = res.candidates.sort((a,b) => a.graduate_date - b.graduate_date)
.map(({ graduate_date, ...rest }) => rest)
res.languages = Object.values(res.languages).sort((a,b) => a.lang.localeCompare(b.lang))
console.log(res)

Build array from another array if some key are identical using JavaScript

I have an array of data. Some of the key in the array are same. I would like to create a new array based on the key and add the other data.
This is my array
var myObjOne = [
{
"name":"John",
"id":1,
"car":"maruti"
},
{
"name":"John",
"id":2,
"car":"wolks"
},
{
"name":"John",
"id":3,
"car":"bmw"
},
{
"name":"Peter",
"id":4,
"car":"alto"
},
{
"name":"Peter",
"id":5,
"car":"swift"
}
];
I would like to convert the array in to the below format.
var myObj = [
{
"name":"John",
"items": [
{ "id":1, "car":"maruti" },
{ "id":2, "car":"wolks" },
{ "id":3, "car":"bmw" }
]},
{
"name":"Peter",
"items": [
{ "id":4, "car":"alto" },
{ "id":5, "car":"swift" },
]
}
];
I am working on a node environment.
You can create an object using Array#reduce first which maps name with items, and then create the final array by looping over the intermediate map using a for...of loop:
var source = [{"name":"John","id":1,"car":"maruti"},{"name":"John","id":2,"car":"wolks"},{"name":"John","id":3,"car":"bmw"},{"name":"Peter","id":4,"cars":"alto"},{"name":"Peter","id":5,"cars":"swift"}];
const map = source.reduce((acc, {name, ...obj}) => {
if (!acc[name]) {
acc[name] = [];
}
acc[name].push(obj);
return acc;
}, {});
const result = [];
for (let[name, items] of Object.entries(map)) {
result.push({name, items});
}
console.log(result);
Array.reduce is at rescue.This method accepts an accumulator and current
item. Check in the accumulator if there exist an object where the value of name property is John or Peter
var myObjOne = [{
"name": "John",
"id": 1,
"car": "maruti"
},
{
"name": "John",
"id": 2,
"car": "wolks"
},
{
"name": "John",
"id": 3,
"car": "bmw"
},
{
"name": "Peter",
"id": 4,
"car": "alto"
},
{
"name": "Peter",
"id": 5,
"car": "swift"
}
];
var newObj = myObjOne.reduce(function(acc, curr, currIndex) {
// using findIndex to check if there exist an object
// where the value of the name property is John, Peter
// if it exist it will return the index else it will return -1
let ifNameExist = acc.findIndex(function(item) {
return item.name === curr.name;
})
// if -1 then create a object with name and item property and push
// it to the accumulator
if (ifNameExist === -1) {
let nameObj = {};
nameObj.name = curr.name;
nameObj.items = [];
nameObj.items.push({
id: curr.id,
car: curr.car
})
acc.push(nameObj)
} else {
// if such an object already exist then just update the item array
acc[ifNameExist].items.push({
id: curr.id,
car: curr.car
})
}
return acc;
}, []);
console.log(newObj)
Use .reduce to group by name, and use .find inside the reducer to find if the matching name has already been added:
const input=[{"name":"John","id":1,"car":"maruti"},{"name":"John","id":2,"car":"wolks"},{"name":"John","id":3,"car":"bmw"},{"name":"Peter","id":4,"cars":"alto"},{"name":"Peter","id":5,"cars":"swift"}]
const output = input.reduce((a, { name, ...item }) => {
const foundNameObj = a.find(nameObj => nameObj.name === name);
if (foundNameObj) foundNameObj.items.push(item);
else a.push({ name, items: [item] });
return a;
}, []);
console.log(output);

Categories