Repeat every element in array based on object properties - javascript

I have an array that I'm retrieving from an API. The array looks like this:
[{
"name": "Rachel",
"count": 4,
"fon": "46-104104",
"id": 2
},
{
"name": "Lindsay",
"count": 2,
"fon": "43-053201",
"id": 3
},
{
"name": "Michael",
"count": 5,
"fon": "46-231223",
"id": 4
}]
Then I loop through the array to create an array containing only the names.
function buildName(data) {
for (var i = 0; i < data.length; i++) {
nameList.push(data[i].name)
}
}
This also works so far, but I would like to create an array in which each name occurs as often as the object count says.
For example, the name Michael should appear five times in the array and Lindsay twice.
[
"Rachel",
"Rachel",
"Rachel",
"Rachel",
"Lindsay",
"Lindsay",
"Michael",
"Michael",
"Michael",
"Michael"
"Michael"
]

For each object create a new array using count, and then fill it with the name.
If you use flatMap to iterate over the array of objects. It will return a new array of nested objects but then flatten them into a non-nested structure.
const data=[{name:"Rachel",count:4,fon:"46-104104",id:2},{name:"Lindsay",count:2,fon:"43-053201",id:3},{name:"Michael",count:5,fon:"46-231223",id:4}];
const out = data.flatMap(obj => {
return new Array(obj.count).fill(obj.name)
});
console.log(out);

I've upgraded your functions but you can use the map method
function buildName(data){
for (let i = 0; i < data.length; i++){
let numToLoop = data[i].count
let name = data[i].name
for (let z = 0; z < +numToLoop; z++){
nameList.push(name)
}
}
}

Use an inner while loop inside the for loop:
const data = [{
"name": "Rachel",
"count": 4,
"fon": "46-104104",
"id": 2
},
{
"name": "Lindsay",
"count": 2,
"fon": "43-053201",
"id": 3
},
{
"name": "Michael",
"count": 5,
"fon": "46-231223",
"id": 4
}]
function buildName(data){
const result = [];
for (let i = 0; i < data.length; i += 1) {
let item = data[i];
let count = item.count;
while (count > 0) {
result.push(item.name);
count -= 1;
}
}
return result;
}
console.log(buildName(data));

Just add an inner loop with as many iterations as the "count" property in the object:
function buildName(data) {
const nameList = [];
for (var i = 0; i < data.length; i++) {
for (let j = 0; j < data[i].count; j++) {
nameList.push(data[i].name);
}
}
return nameList;
}

For fun
import { pipe } from 'fp-ts/lib/function';
import { chain, replicate } from 'fp-ts/lib/Array';
const arr = ...
const result = pipe(
arr,
chain(i => replicate(i.count, i.name))
);

You can use .flapMap() for that:
const arr = [{ "name": "Rachel", "count": 4, "fon": "46-104104", "id": 2 }, { "name": "Lindsay", "count": 2, "fon": "43-053201", "id": 3 }, { "name": "Michael", "count": 5, "fon": "46-231223", "id": 4 }];
const result = arr.flatMap(({count, name}) => Array(count).fill(name));
console.log(result);
Effectively you turn every element into an array of the the name property repeated count times which is then flattened into a single array.

It can be done via creating an array with repeated names in this way:
Array(count).fill(name)
Then you have to spread it into resulting array.
You can try this one-liner
const getNames = (data) =>
data.reduce(
(names, { name, count }) => [...names, ...Array(count).fill(name)],
[]
)
Note that a pure function is presented here, which is generally the preferred way of writing code. However, updating your example code might look like this
const getNames = (data) =>
data.reduce(
(names, { name, count }) => [...names, ...Array(count).fill(name)],
[]
)
function buildName(data) {
nameList = getNames(data)
}

Related

Javascript array format

I have two javascript array
let a=[{id:1},{id:2},{id:3}];
let b=[{count:35, name:'test', age:12}, {count:45 name:'test2', age:9}];
and two array push into i need final Array format is
[{count:35,name:'test', age,12, id:1} , {count:35, name:'test', age:12,id:2},{count:35, name:'test', age:12,id:3},{count:45,name:'test', age,9, id:1} , {count:45, name:'test', age:9,id:2},{count:45, name:'test', age:9,id:3} ]
And i trying
for ( var index=0; index<b.length; index++ ) {
for ( var j=0; j<a.length; j++ ) {
for (const [key, value] of Object.entries(a[j])) {
//if(j==index)
b[index][key]=value;
k[j]=b[index];
}
console.log( b[index]);
c.push(b[index]);
}
}
console.log(c);
and it shows final value is
please any body help to fix the problem
Your current implementation, is essentially updating the same b objects over and over, x number of times, changing their IDs each time through, ending at 3.
You need to "clone" the objects, so they are separate objects, like so:
let p = []; // Permutations
let a = [{id:1},{id:2}]; // Renditions...
// Presets...
let b = [{count:35, name:'test', age:12}, {count:45, name:'test2', age:9}];
// for each Rendition
a.forEach(function(a) {
// for each Preset
b.forEach(function(b){
// generate Permutation and Clone, object of b with additional id prop
p.push(Object.assign({id: a.id}, b)); // <-- Cloned here...
});
});
console.log(p)
for the sake of clarity, you might consider changing the id prop to grouping or group or group_id.
You could use flatMap:
let a=[{id:1},{id:2},{id:3}];
let b=[{count:35, name:'test', age:12}, {count:45, name:'test2', age:9}];
let m = b.flatMap(itemB => a.map(itemA => ({...itemB, ...itemA })))
console.log(m);
I got your problem. The thing is that you are mutating the same b object every iteration, that's why in the end you get id = 3 for every element. You can use a deep copy of b in each cycle, and the error should be gone.
Here's one way to deep clone, not the most efficient but it is very clear:
const bCopy = JSON.parse(JSON.stringify(b[index]));
for (const [key, value] of Object.entries(a[j])) {
//if(j==index)
bCopy[key]=value;
k[j]=bCopy; // what's this for?
}
console.log(bCopy);
c.push(bCopy);
Reduce b and for each item b1, reduce a. When reducing a, just assign both b1 and a1 to a new object.
const expectedValue = JSON.stringify(JSON.parse(
document.getElementById('expected').value))
let a = [
{ id: 1 },
{ id: 2 },
{ id: 3 }
]
let b = [
{ count: 35, name: 'test', age: 12 },
{ count: 45, name: 'test2', age: 9 }
]
let c = b.reduce((res, b1) =>
a.reduce((res1, a1) =>
[...res1, Object.assign({}, b1, a1)], res), [])
console.log(JSON.stringify(c) === expectedValue)
.as-console-wrapper { top: 0; max-height: 100% !important; }
#expected { display: none; }
<textarea id="expected">
[
{ "count": 35, "name": "test", "age": 12, "id": 1 },
{ "count": 35, "name": "test", "age": 12, "id": 2 },
{ "count": 35, "name": "test", "age": 12, "id": 3 },
{ "count": 45, "name": "test2", "age": 9, "id": 1 },
{ "count": 45, "name": "test2", "age": 9, "id": 2 },
{ "count": 45, "name": "test2", "age": 9, "id": 3 }
]
</textarea>
Try this changes.
let a=[{id:1},{id:2},{id:3}];
let b=[{count:35, name:'test', age:12}, {count:45, name:'test2', age:9}];
let c=[];
for ( var index=0; index<b.length; index++ ) {
for ( var j=0; j<a.length; j++ ) {
var obj=b[index];
for (const [key, value] of Object.entries(a[j])) {
obj[key]=value;
}
c.push(obj);
}
}
console.log(c);

Comparing information from an API call and an Array

I'm trying to compare the results of an API call to an existing array. Basically, I want to make a function that will loop through the array, then loop through the data from the API to see if there's a match.
Here's an example of the array I'm working with
let array = [ {
"name": "student1",
"id": 134},
{
"name": "student2",
"id": 135},
{
"name": "student3",
"id": 136}
]
Here's my function in JavaScript/jQuery
function getData() {
$.ajax({
url: "www.studentapi.com",
dataType: "json"
}).done(function(data) {
console.log(data)
}
}
The data I get back looks kind of like this:
[ {
"id": 134,
"score": 45},
{
"id": 138,
"score": 67},
{
"id": 139,
"score": 34}
]
I'm trying to find a way to find the matching ids in the array and in the data. So far I've tried:
for (let j =0; j < data.length; j++) {
if (array[j]["id"] === data[j].id) {
console.log("we have a match!")
}
else {
console.log("not a match!");
}
}
But this isn't working. Am I doing something incorrectly over here?
You can use find on an array to find an element that matches some conditional.
The below logic also uses arrow functions, but could be changed to use normal function(){}
let array = [
{
"name": "student1",
"id": 134
},
{
"name": "student2",
"id": 135
},
{
"name": "student3",
"id": 136
}
];
let data = [
{
"id": 134,
"score": 45
},
{
"id": 138,
"score": 67
},
{
"id": 139,
"score": 34
}
];
let studentData = array.map(student=>{
student.data = data.find(record=>record.id === student.id) || {};
return student;
});
console.log(studentData);
I would use the javascript filter function.
let matchingStudents = array.filter(student => {
return data.find(jsonData => student.id === jsonData.id);
});
There matchingStudents would hold all students present in the first array that are present in the second.
If you are wondering about the syntax, this is ES6. Next generation javascript. To write it in old javascript it'd be:
var matchingStudents = array.filter(function(student){
return data.find(function(jsonData){ return student.id === jsonData.id});
}
To specifically answer your question Am I doing something incorrectly over here?
Your search code here assumes that array and data will contain the exact same ids in the exact same order:
for (let j =0; j < data.length; j++) {
if (array[j]["id"] === data[j].id) {
Based on the sample data you provided, this isn't the case; you can't always compare array[j] to data[j] to match ids because (for example) it's possible you need to match array[4] to data[6].
One solution to this problem is to use a nested loop:
for (let i = 0; i < array.length; i++) {
for (let j = 0; j < data.length; j++) {
if (array[i].id === data[j].id) {
This way you'll compare every entry in array to every entry in data when looking for matches. (This is similar to what the solutions suggesting array.map and data.find are doing, with some smart early-out behavior.)
Another approach would be to sort both lists and step forward through them together.
let array = [
{ "id": 134, "name": "student1" },
{ "id": 139, "name": "student2" },
{ "id": 136, "name": "student3" }
];
let data = [
{ "id": 134, "score": 45 },
{ "id": 138, "score": 67 },
{ "id": 139, "score": 34 }
];
array.sort((a, b) => a.id - b.id)
data.sort((a, b) => a.id - b.id)
let data_i = 0;
for (let array_i = 0; array_i < array.length; array_i++) {
while (data[data_i].id < array[array_i].id) {
data_i++;
}
if (data_i < data.length && data[data_i].id === array[array_i].id) {
console.log(`Matched ${array[array_i].name} to score ${data[data_i].score}`);
} else {
console.log(`No match found for ${array[array_i].name}`);
}
}

How to add a new key to multiple indices of an array of objects?

I've got an array of three people. I want to add a new key to multiple objects at once based on an array of indices. Clearly my attempt at using multiple indices doesn't work but I can't seem to find the correct approach.
var array = [
{
"name": "Tom",
},
{
"name": "Dick",
},
{
"name": "Harry",
}
];
array[0,1].title = "Manager";
array[2].title = "Staff";
console.log(array);
Which returns this:
[
{
"name": "Tom",
},
{
"name": "Dick",
"title": "Manager"
},
{
"name": "Harry",
"title": "Staff"
}
]
But I'd like it to return this.
[
{
"name": "Tom",
"title": "Manager"
},
{
"name": "Dick",
"title": "Manager"
},
{
"name": "Harry",
"title": "Staff"
}
]
You cannot use multiple keys by using any separator in arrays.
Wrong: array[x, y]
Correct: array[x] and array[y]
In your case, it will be array[0].title = array[1].title = "manager";
1st method::
array[0].title = "Manager";
array[1].title = "Manager";
array[2].title = "Staff";
array[0,1] will not work.
2nd method::
for(var i=0;i<array.length;i++) {
var msg = "Manager";
if(i===2) {
msg = "Staff"
}
array[i].title = msg
}
You can use a helper function like this
function setMultiple(array, key, indexes, value)
{
for(i in array.length)
{
if(indexes.indexOf(i)>=0){
array[i][key] = value;
}
}
}
And then
setMultiple(array, "title", [0,1], "Manager");
Try this: `
for (var i=0; var<= array.length; i++){
array[i].title = "manager";
}`
Or you can change it around so var is less than or equal to any n range of keys in the index.
EDIT: instead make var <= 1. The point is to make for loops for the range of indices you want to change the title to.
Assuming that you have a bigger set of array objects.
var array = [
{
"name": "Tom",
},
{
"name": "Dick",
},
{
"name": "Harry",
},
.
.
.
];
Create an object for the new keys you want to add like so:
let newKeys = {
'Manager': [0,2],
'Staff': [1]
}
Now you can add more such titles here with the required indexes.
with that, you can do something like:
function addCustomProperty(array, newKeys, newProp) {
for (let key in newKeys) {
array.forEach((el, index) => {
if (key.indexOf(index) > -1) { // if the array corresponding to
el[newProp] = key // the key has the current array object
} // index, then add the key to the
}) // object.
}
return array
}
let someVar = addCustomProperty(array, newKeys, 'title')

Merging linked Data in Array in Javascript

I have a simple task of rearranging a couple of Arrays in a JSON, so ractive.js can handle it better. But I got carried away a bit, and the outcome was not particularly satisfactory.
An example of my initial Array:
[{
"_id": 1,
"type": "person",
"Name": "Hans",
"WorksFor": ["3", "4"],
}, {
"_id": 2,
"type": "person",
"Name": "Michael",
"WorksFor": ["3"],
}, {
"_id": 3,
"type": "department",
"Name": "Marketing"
}, {
"_id": 4,
"type": "department",
"Name": "Sales"
}, {
"_id": 5,
"type": "person",
"Name": "Chris",
"WorksFor": [],
}]
So with a given Department I wanted a method in ractive to give me all Persons who work in this Department (with a list of Departments they work for). Something like:
[{
"_id": 1,
"type": "person",
"Name": "Hans",
"WorksFor": ["3", "4"],
"Readable": ["Marketing", "Sales"]
}, {
"_id": 2,
"type": "person",
"Name": "Michael",
"WorksFor": ["3"],
"Readable": ["Sales"]
}]
The function that somehow came to life was similar to this:
function imsorryforthis() {
let output = [];
let tempdocs = this.get('docs'); //as this happens in a ractive method,
//"this.get" is neccesary for binding
for (var i = 0; i < tempdocs.length; i++) {
if (_.contains(tempdocs[i].WorksFor, givenDepartment)) { //I used underscore.js here
let collectedDepartmentData = [];
if (tempdocs[i].WorksFor.length > 0) {
for (var f = 0; f < tempdocs[i].WorksFor.length; f++) {
for (var g = 0; g < tempdocs.length; g++) {
if (tempdocs[i].WorksFor[f] == tempdocs[g]._id) {
let actualDepartmentData = {};
actualDepartmentData = tempdocs[g];
collectedDepartmentData.push(actualDepartmentData);
}
}
}
}
tempdocs[i].Readable = collectedDepartmentData;
output.push(tempdocs[i]);
}
}
return output;
}
I've put it in a Fiddle as well to make it better readable.
Due to the fact that somehow this monstrosity does work (I was astonished myself), it feels like scratching your left ear with your right hand over your head (while being constantly shouted at by a group of desperate mathematicians).
Maybe anybody knows a more presentable and smarter approach (or a way to compile JavaScript so this never sees the light of day again).
Construct a map department_id => department_name first:
let departments = {};
for (let x of data) {
if (x.type === 'department') {
departments[x._id] = x.Name;
}
}
Then, iterate over Persons and populate Readable arrays from that map:
for (let x of data) {
if (x.type === 'person') {
x.Readable = x.WorksFor.map(w => departments[w]);
}
}
Finally, extract Persons for the specific Department:
personsInSales = data.filter(x =>
x.type === 'person' && x.WorksFor.includes('3'));
Firstly, your data structure does not have a good design. You should not be returning person and department in the same array. If possible, try to redesign the initial data structure to make it more modular, by separating out the people and department into separate structures. However if you are stuck with this same data structure, you can write the code a little better. Please find the code below. Hope it helps!
function mapPeopleDepartment() {
var deptMap = {},peopleList = [];
//Iterate through the initialArray and separate out the department into a hashmap deptMap and people into a new peopleList
for(var i=0; i < initArray.length; i++) {
var obj = initArray[i];
obj.type == "department" ? deptMap[obj._id] = obj.Name : peopleList.push(obj);
}
//Iterate through the peopleList to map the WorksFor array to a Readable array
for(var i=0; i < peopleList.length; i++) {
var person = peopleList[i];
person.Readable = _.map(person.WorksFor, function(dept){return deptMap[dept]});
}
return peopleList;
}

complex grouping and merging array as properties

I wish to put an array into other's array as proproties by matching their common properties. I want jobDetails's uId to match with job's uId. Possible?
var job = [{
"uId": 1
}, {
"uId": 2
}]
var jobDetails = [{
"uId": 1,
"salary": 5000
}, {
"uId": 2,
"salary": 5000
}]
is it possible to produce something like
var job = [{
"uId": 1,
"salary": [{
"uId": 1,
"salary": 5000
}]
}, {
"uId": 2,
"salary": [{
"uId": 2,
"salary": 5000
}]
}];
You may try something like this: http://jqversion.com/#!/XWFtbQb
for (var i = 0; i < job.length; i++) {
for (var j = 0; j < jobDetails.length; j++) {
if (job[i].uId == jobDetails[j].uId) {
job[i].salary = jobDetails[j];
}
}
}
console.log(job);
This is not a pure javascript solution, but I like to use underscore.js for this kind of typical actions on collections:
http://jsfiddle.net/FPwq7/
var finalCollection = [];
_.each(job, function(model){
var obj = _.findWhere(jobDetails, {uId: model.uId});
_.extend(model, {'salaray': obj});
finalCollection.push(model);
});
console.log(finalCollection);
I found that this Javascript utility belt takes care of some heavy lifting, and it makes the code a bit more pleasant to read than reading dry loops.
Yes possible , you need to play with both json objects
var array = [];
var object = {}
$.each( job, function ( k , kal ) {
$.each( jobDetails , function ( i , val) {
object.uId = i;
object.salary = val;
});
array.push(object);
});
console.log(JSON.stringify(array));

Categories