Javascript Loops: for-loop works but not map? - javascript

I'm working with mockData for a web app and I'm trying to loop over nested objects. My problem is that a for loop works but not array.map and don't know why.
Here is the for loop:
for (let i = 0; i < fakeChartData.length; i++) {
for (let j = 0; j < fakeChartData[i].poll.length; j++) {
if (fakeChartData[i].poll[j].id === id) {
return fakeChartData[i].poll[j]
}
}
}
And here is the map loop:
fakeChartData.map(data => {
data.poll.map(data => {
if (data.id === id) {
return data;
}
});
});
My Data structure:
fakeChartData = [
{
id: '232fsd23rw3sdf23r',
title: 'blabla',
poll: [{}, {}]
},
{
id: '23dgsdfg3433sdf23r',
title: 'againBla',
poll: [{}, {}]
}
];
I'm trying to load the specific object with the id passed to it on onClick method.
Here is the full function:
export const fetchPollOptById = (id) =>
delay(500).then(() => {
for (let i = 0; i < fakeChartData.length; i++) {
for (let j = 0; j < fakeChartData[i].poll.length; j++) {
if (fakeChartData[i].poll[j].id === id) {
return fakeChartData[i].poll[j]
}
}
}
});

A return statement inside a for loop causes your function to return. However, a return statement inside a .map() function's callback only returns the callback and this returned value is then placed in the new array. Please see the documentation.If you really want to be using .map(), you could do it like this:
export const fetchPollOptById = (id) => {
var result;
fakeChartData.map(data => {
data.poll.map(data => {
if (data.id === id) {
result = data;
return data;
}
});
});
return result;
}
note: I also assume that your poll objects have an id field like this:
fakeChartData = [
{
id: '232fsd23rw3sdf23r',
title: 'blabla',
poll: [
{id: 'pollId1', otherField: 'blah'},
{id: 'pollId2', otherField: 'blah'}
]
},
{
id: '23dgsdfg3433sdf23r',
title: 'againBla',
poll: [
{id: 'pollId3', otherField: 'blah'},
{id: 'pollId4', otherField: 'blah'}
]
}
];
You can then get the poll data like this:
fetchPollOptById("pollId3"); //returns {id: "pollId3", otherField: "blah"}

If I'm right about what you're trying to do, this should work:
return fakeChartData.reduce((acc, data) => acc.concat(data.poll), [])
.filter(pollObj => pollObj.id === id)[0]
First it makes an array containing all the poll objects from different data objects, then it filters them to find the one with the correct id and returns that object.
As to why your approach using map does not work: you are using it in the wrong way. What map does it to take a function and apply it to every member of an array.
Here's an array and function kind of like yours:
const arr = [1,2,3]
const getThingById(id) => {
var mappedArray = arr.map(x => {
if(x === id) return x
})
console.log(mappedArray) // [3]
}
getThingById(3) // undefined
This won't work. getThingById has no return statement. The return statement return x is returning something from the function that is passed into map. Basically, you shouldn't be using map to do what you're trying to do. map is for when you want to return an array.

Try this
fakeChartData.map(data => {
var result = data.poll.map(data => {
if (data.id === id) {
return data;
}
});
return result;
});
It should work. And yeah you should use find() instead of map() I think.

A bit long implementation:
let results = fakeChartData.map(data => {
let innerResult = data.poll.filter(data => {
if (data.id === id) {
return data;
}
return innerResult.length ? innerResult[0] : null;
});
})
.filter(x => (x !== null));
let whatYouwant = results.lenght ? results[0] : null;
If you can use find() it would look nicer, but that depends on what browsers you need to support

Related

How to insert key in object based on Id

I have an array of object, I want to add key in my specifi object of array when Id is matched. I have tried this:
this.data.forEach(value => {
if (value.Id === attachmentDataId) {
AttachmentTypeId: this.attachmentRecord.AttachmentType;
}
});
But it's not working and it's not giving any error also
Try this out :
let data = [{ id: 1 }, { id: 5 }];
const attachmentDataId = 5;
const attachmentRecord = { AttachmentType: "AttachmentType" };
data.forEach(value => {
if (value.id === attachmentDataId) {
value.AttachmentTypeId = attachmentRecord.AttachmentType;
}
});
The stackblitz example: https://stackblitz.com/edit/js-nrhouh
You could use the index parameter of forEach function to access the specific object of the array.
this.data.forEach((value, i) => {
if (value.Id === attachmentDataId) {
this.data[i] = {
...this.data[i],
AttachmentTypeId: this.attachmentRecord.AttachmentType
};
}
});
Inside the if block, you could also instead do
this.data[i]['AttachmentTypeId'] = this.attachmentRecord.AttachmentType;
I just find using the spread operator cleaner.
use javascript map() method.
Map() return a new array, it takes a callback, iterates on each element in that array
const updatedData = data.map(res => {
if(res.id === attachmentDataId) {
res.AttachmentTypeId = attachmentRecord.AttachmentType;
}
return res
})

How to rename an object property values removing a specific object from the list

I have a list of object
let table = [{id:4,val:"21321"},{id:5,val:"435345"},{id:6,val:"345345"}]
I want to rename the id value after removing an object from the list which has a specific id value(for example id:5)
I am using array filter method
table.filter((element,index)=>{
if(element.id!==5){
element.id=index
return element
}else{
index+1
}
return null
})
I am expecting a return value
[{id: 0,val: "21321"},{id: 1,val: "345345"}]
but i am getting this
[{id: 0, val: "21321"},{id: 2, val: "345345"}]
Note: I know i can use filter method to remove the specific object and than use map method to rename the id value but i want a solution where i have to use only one arrow function
You can use array#reduce to update the indexes and remove elements with given id. For the matched id element, simply return the accumulator and for other, add new object with val and updated id.
const data = [{ id: 4, val: "21321" }, { id: 5, val: "435345" }, { id: 6, val: "345345" }],
result = data.reduce((res, {id, val}) => {
if(id === 5) {
return res;
}
res.push({id: res.length + 1, val});
return res;
}, []);
console.log(result)
This would do it:
let table = [[{id:4,val:"21321"},{id:5,val:"435345"},{id:6,val:"345345"}]];
let res=table[0].reduce((a,c)=>{if(c.id!=5){
c.id=a.length;
a.push(c)
}
return a}, []);
console.log([res])
The only way to do it with "a single arrow function" is using .reduce().
Here is an extremely shortened (one-liner) version of the same:
let res=table[0].reduce((a,c)=>(c.id!=5 && a.push(c.id=a.length,c),a),[]);
Actually, I was a bit premature with my remark about the "only possible solution". Here is a modified version of your approach using .filter() in combination with an IIFE ("immediately invoked functional expression"):
table = [[{id:4,val:"21321"},{id:5,val:"435345"},{id:6,val:"345345"}]];
res= [ (i=>table[0].filter((element,index)=>{
if(element.id!==5){
element.id=i++
return element
} }))(0) ];
console.log(res)
This IIFE is a simple way of introducing a persistent local variable i without polluting the global name space. But, stricly speaking, by doing that I have introduced a second "arrow function" ...
It probably is not the best practice to use filter and also alter the objects at the same time. But you would need to keep track of the count as you filter.
let table = [[{id:4,val:"21321"},{id:5,val:"435345"},{id:6,val:"345345"}]]
const removeReorder = (data, id) => {
var count = 0;
return data.filter(obj => {
if (obj.id !== id) {
obj.id = count++;
return true;
}
return false;
});
}
console.log(removeReorder(table[0], 5));
It is possible to achieve desired result by using reduce method:
const result = table.reduce((a, c) => {
let nestedArray = [];
c.forEach(el => {
if (el.id != id)
nestedArray.push({ id: nestedArray.length, val: el.val });
});
a.push(nestedArray);
return a;
}, [])
An example:
let table = [[{ id: 4, val: "21321" }, { id: 5, val: "435345" }, { id: 6, val: "345345" }]]
let id = 5;
const result = table.reduce((a, c) => {
let nestedArray = [];
c.forEach(el => {
if (el.id != id)
nestedArray.push({ id: nestedArray.length, val: el.val });
});
a.push(nestedArray);
return a;
}, [])
console.log(result);
The main issue in your code is filter method is not returning boolean value. Use the filter method to filter items and then use map to alter object.
let table = [
[
{ id: 4, val: "21321" },
{ id: 5, val: "435345" },
{ id: 6, val: "345345" },
],
];
const res = table[0]
.filter(({ id }) => id !== 5)
.map(({ val }, i) => ({ id: i, val }));
console.log(res)
Alternatively, using forEach with one iteration
let table = [[{id:4,val:"21321"},{id:5,val:"435345"},{id:6,val:"345345"}]]
const res = [];
let i = 0;
table[0].forEach(({id, val}) => id !== 5 && res.push({id: i++, val}));
console.log(res)

Convert to Object and Adding property to object of array

I want to make filter by date with this object of array
const mapDateRange = () => {for (let elem in catchData) {
let x = {startDate:catchData[elem][0],finishDate:catchData[elem][1]};
return x;
}};
but its only catch one object of array
this is latest data has processing
const data = {
["01-08-2019", "08-08-2019"],
["08-08-2019", "15-08-2019"],
["15-08-2019", "22-08-2019"],
["22-08-2019", "29-08-2019"]
};
this is what i expected
const data = [
{
startDate:"01-08-2019", finisDate:"08-08-2019"
},
{
startDate:"08-08-2019", finisDate:"15-08-2019"
},
{
startDate:"15-08-2019", finisDate:"22-08-2019"
},
{
startDate:"22-08-2019", finisDate:"29-08-2019"
}
];
So there are a few problems in the code you wrote:
Your data started as an object ({}), but its built as an array, so I corrected that.
Your function mapDateRange uses catchData but it does not exist anywhere so I made the function except an argument, which will be the catchData.
Most important: You returned x which is only 1 item in the array of data. So I created an empty array and pushed x values to the array.
const data = [
["01-08-2019", "08-08-2019"],
["08-08-2019", "15-08-2019"],
["15-08-2019", "22-08-2019"],
["22-08-2019", "29-08-2019"]
];
const mapDateRange = (catchData) => {
let new_data = [];
for (let elem in catchData) {
let x = {
startDate: catchData[elem][0],
finishDate: catchData[elem][1]
};
new_data.push(x);
}
return new_data;
};
console.log(mapDateRange(data));
const data = [
["01-08-2019", "08-08-2019"],
["08-08-2019", "15-08-2019"],
["15-08-2019", "22-08-2019"],
["22-08-2019", "29-08-2019"]
];
const mapDataRange = (data) => {
const result = [];
data.forEach((item) => {
const x = { 'startDate': item[0], 'finishDate': item[1] };
result.push(x);
});
return result;
}
console.log(mapDatatRange(data));
In this way you will get your desire result by using map function
data = data.map((obj) => {
return {
startDate: obj[0],
finishDate: obj[1]
}
});
console.log(data)
try to do with .map and array destructuring with ES6 syntax
data.map(([ startDate, finishDate ]) => { startDate, finisDate })

Filter data inside array object of array object using javascript

I am trying to filter data inside array object of array object, Please find below code for more information.
var data = [
{
name:'testdata1',
subdata:[{status:'fail'},{status:'success'}]
},
{
name:'testdata2',
subdata:[{status:'fail'},{status:'success'}]
}
]
Expected Data:
var successdata = [
{
name:'testdata1',
subdata:[status:'success'}]
},
{
name:'testdata2',
subdata:[status:'success'}]
}
];
var FailureData =[
{
name:'testdata1',
subdata:[{status:'fail'}]
},
{
name:'testdata2',
subdata:[{status:'fail'}]
}
];
I missed curly braces,So i am updating
Hope this helps.
const data = [{
name: 'testdata1', subdata: [{status: 'fail'}, {
status:
'success'
}]
},
{
name: 'testdata2', subdata:
[{status: 'success'}, {status: 'fail'}]
}
];
const filterData = (data, status) => data.reduce((acc, val) => {
const sub = val.subdata.map((v) => v.status === status ? ({ name: val.name, subdata: [v] }) : null).filter(f => f !== null);
return acc.concat(sub);
}, []);
const successData = filterData(data, 'success');
const failureData = filterData(data, 'fail');
console.log('successData', successData);
console.log('failureData', failureData);
You could map your arrays using Array.map():
var successData = data.map(item => ({name: item.name, subdata:[{status:'success'}]})
What I guess you want to do is filter the array based on subdata status.
I also guess that what subdata should have is just the status property and your code would be: var data = [{name:'testdata1',subdata:[{status:'fail'},{status:'success'}] }.
Then you want to look in the subdata array and find which data have success and failure in them.
So what you could be looking for is this:
var successData = data.filter(sdata => {
var successFlag=false;
sdata.subdata.forEach(subdata=>{
if (subdata.status==='success'){
successFlag = true;
}
}
return successFlag;
}
The same with the failureData.
For more information you could check the Array.prototype.filter function:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
P.S. As mentioned in a comment to your question as well, your subdata array cannot be an object with two of the same property
var data = [{name:'testdata1',subdata:[{status:'fail'}, {status:'success'}] },{name:'testdata2',subdata:[{status:'success'}, {status:'fail'}] }]
var successData = filterByStatus('success', data);
var failureData = filterByStatus('fail', data);
function filterByStatus(status, data) {
return data.map(d => {
var newObj = Object.assign({}, d);
newObj.subdata = newObj.subdata.filter(s => s.status === status);
return newObj;
});
}
console.log('successData', successData);
console.log('failureData', failureData);
one of possible ways to do what you want if you have one success property in your object

How to get from an array of objects all unique values of a property that is an array itself

This answer is already close, and there are some answers how to get unique values in an array (remove duplicates,)though I can't make it work for the case where it is about an array of objects, and the property that should be filtered is an array. Sorry, I am a JS newbie. Thanks for the help.
I have an array of objects like this
const posts = [
post1: {
id: 1,
title: 'One',
tags: ['tagA', 'tagB']
},
post2: {
id: 2,
title: 'Two',
tags: ['tagB', 'tagC']
},
post3: {
id: 3,
title: 'Three',
tags: ['tagB', tagC, tagD]
]
What I would need is an array of all unique tags ... in the case above with an expected output like this:
// [tagA, tagB, tagC, tagD]
EDIT / UPDATE
The key in the array of objects is used to manage the state of the react component... e.g.
constructor(props) {
super(props);
this.state = {
posts: []
};
}
...
updatePost = (key, updatedPost) => {
//1. Take copy of the current this.state.
const posts = {...this.state.texts};
//2. Update that state
posts[key] = updatedPost;
//3. Set that to state
const options = { encrypt: false }
putFile(postsFileName, JSON.stringify(posts), options)
.then(() => {
this.setState({
posts: posts
})
})
};
Assuming that the input is on [ {} , {} ] format:
You can use concat and map to flatten your array. Use new Set to get the unique values.
const posts = [{"id":1,"title":"One","tags":["tagA","tagB"]},{"id":2,"title":"Two","tags":["tagB","tagC"]},{"id":3,"title":"Three","tags":["tagB","tagC","tagD"]}];
var result = [...new Set([].concat(...posts.map(o => o.tags)))];
console.log(result);
If the variable is an object ( {a:{} , b:{} } ) , you can use Object.values to convert the object into an array.
const posts = {"post1":{"id":1,"title":"One","tags":["tagA","tagB"]},"post2":{"id":2,"title":"Two","tags":["tagB","tagC"]},"post3":{"id":3,"title":"Three","tags":["tagB","tagC","tagD"]}}
var result = [...new Set([].concat(...Object.values(posts).map(o => o.tags)))];
console.log(result);
You can reduce your posts and iterate over the tags and push those to the result that you haven't encountered already:
const posts = [
{
id: 1,
title: "One",
tags: ["tagA", "tagB"]
},
{
id: 2,
title: "Two",
tags: ["tagB", "tagC"]
},
{
id: 3,
title: "Three",
tags: ["tagB", "tagC", "tagD"]
}
];
const uniqueTags = posts.reduce((result, post) => {
post.tags.forEach(tag => {
if (!result.includes(tag)) {
result.push(tag);
}
});
return result;
}, []);
console.log(uniqueTags);
This is assuming you know that the array key is always 'tags'.
let filter = {};
let result = [];
posts.forEach(post => {
const tags = post['tags'];
tags.forEach(tag => {
if (!filter.hasOwnProperty(tag)) {
result.push(tag);
filter[tag] = true;
}
});
});
with jquery you can do something similar to this (not Tested):
var results = [];
$.each(myObject, function(key,valueObj){
var check.isArray(obj);
if(check){
alert(key + "/" + valueObj );
/*replace repeat*/
var sorted_check = check.slice().sort(); // You can define the comparing function here.
// JS by default uses a crappy string compare.
// (we use slice to clone the array so the
// original array won't be modified)
for (var i = 0; i < sorted_check.length - 1; i++) {
if (sorted_check[i + 1] == sorted_check[i]) {
results.push(sorted_check[i]);
}
}
}
});
and a good way with indexof:
Array.prototype.unique = function() {
var a = [];
for ( i = 0; i < this.length; i++ ) {
var current = this[i];
if (a.indexOf(current) < 0) a.push(current);
}
this.length = 0;
for ( i = 0; i < a.length; i++ ) {
this.push( a[i] );
}
return this;
}
Array.prototype.unique = function() {
var a = [];
for ( i = 0; i < this.length; i++ ) {
var current = this[i];
if (a.indexOf(current) < 0) a.push(current);
}
return a;
}
And Follow UP:
Array.prototype.unique = function(mutate) {
var unique = this.reduce(function(accum, current) {
if (accum.indexOf(current) < 0) {
accum.push(current);
}
return accum;
}, []);
if (mutate) {
this.length = 0;
for (let i = 0; i < unique.length; ++i) {
this.push(unique[i]);
}
return this;
}
return unique;
}
If you want to use a functional library like Ramda.js you can do this:
const posts = [
{
id: 1,
title: 'One',
tags: ['tagA', 'tagB'],
},
{
id: 2,
title: 'Two',
tags: ['tagB', 'tagC'],
},
{
id: 3,
title: 'Three',
tags: ['tagB', 'tagC', 'tagD'],
},
];
var unique = R.uniq(R.flatten(R.map(R.prop('tags'), posts)))
console.log(unique)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

Categories