ImmutableJS - update value in a List - javascript

I have a Map like this (in ImmutableJS):
{arrayOfValues: [
{one: {inside: 'first in array'}},
{one: {inside: 'second in array'}}
]}
And I want to update the value "inside" in the second entry in the "arrayOfValues" array. How can I do it? This is what I have now and it says "Uncaught Error: invalid keyPath"
theMap.update('arrayOfValues',(list)=>{
return list.setIn([1,'one','inside'],'updated value');
})
I also tried directly this and it didn't work:
theMap.setIn(['arrayOfValues',1,'one','inside'],'updated value');
After several hours of looking for the solution, I appreciate any help. Thank you.

What you are doing is correct (see this JSBin).
const orig = Immutable.fromJS({
arrayOfValues: [
{ one: { inside: 'first in array' } },
{ one: { inside: 'second in array' } },
]
});
const updated = orig.setIn(['arrayOfValues', 1, 'one', 'inside'], 'updated value');
console.log(updated.toJS());
// {
// arrayOfValues: [
// { one: { inside: 'first in array' } },
// { one: { inside: 'second in array' } },
// ]
// }
When you call orig.setIn(), it doesn't modify orig directly. That's the whole purpose of this Immutable library. It doesn't mutate the existing data but creates a new one from the existing one.

Your setIn example works as you should see in this plunkr:
http://plnkr.co/edit/1uXTWtKlykeuU6vB3xVO?p=preview
Perhaps you are assuming the value of theMap will be changed as a result of the setIn?
As these structures are immutable, you must capture the modified value in a new variable as var theMap2 = theMap.setIn(['arrayOfValues',1,'one','inside'],'updated value');

activePane is the index of Object in Array(List) that I had to modify
case CHANGE_SERVICE:
var obj = {
title: '1212121 Tab',
service: '',
tagName: '',
preDefinedApi: '',
methodType: '',
url: '',
urlParams: [{
label: '',
name: '',
value: '',
}],
headers: [{
label: '',
name: '',
value: '',
}],
};
var activePane = state.get('activePane');
var panes = state.setIn(['panes', activePane, 'service'], action.val);
return state.setIn(['panes', activePane, 'service'], action.val);

Related

how to add element to object in array javascript

I have an array of objects and it is structured as so:
let array1 = [{}]
array1.push({
title: 'myTitle1',
extendedProps: {
field1: 'hello'
}
})
now what I am trying is do an axios.get request and use the data returned to add a new element into array1. I am successfully retrieving the data from the axios request, however, when I try to push the data: res.data[i]._doc.userIsReg, like so:
array1.push({
...array1,
extendedProps: {
...array1.extendedProps,
userIsReg: true
}
})
as you can see, I am using the spread functionaluty to include the current data from array1 into the array and then I try to append a new element userIsReg to the object extendedProps. Now, I assumed this would work, however, when I do this, it creates new object entries within the array and includes everything from inside the array currently (from spread functionality) and adds new entries with the userIsReg in there.
so to be more clear, I start with this:
[{
title: 'myTitle1',
extendedProps: {
field1: 'hello'
}
},
{
title: 'myTitle2',
extendedProps: {
field1: 'hello2'
}
},
]
and then once i do the array1.push with the spread functionality, i get this:
[{
title: 'myTitle1',
extendedProps: {
field1: 'hello'
}
},
{
title: 'myTitle2',
extendedProps: {
field1: 'hello2'
}
},
{
title: 'myTitle1',
extendedProps: {
field1: 'hello',
userIsReg: true
}
},
{
title: 'myTitle2',
extendedProps: {
field1: 'hello2',
userIsReg: true
}
},
]
so basically doubles everything up, instead of appending it to the current array. how would i fix this?
You can do something like following once you have response
array1.map(element => ({...element, userIsReg: true}))
It will add userIsReg flag to each object of your array.
Now as you want it inside extendedProps, you can use following
array1.map(element => (
{...element,
extendedProps: {
...element.extendedProps,
userIsReg: true
}}))
as you defined your array in let type you can do it in this way:
array1 = [...array1.filter(item=> item.title === selected_item.title ),{
...selected_item,userIsReg: true
}]
what I did is to just remove the previous element and add a new one with a new value
if you want to preserve order of array you can sort that
Try this way :
let array1 = [{
title: 'myTitle1',
extendedProps: {
field1: 'hello'
}
},
{
title: 'myTitle2',
extendedProps: {
field1: 'hello2'
}
}];
array1.forEach(obj => {
obj.extendedProps['userIsReg'] = true // this value is coming from API
});
console.log(array1);

Filtering and maping in nested arrays

My task is to filter objects by values in nested arrays. like in example below:
const array = [
{
authorId: '62222a1cea00a0601f200142',
description: [
[
{
title: 'English description',
paragraph: 'And english paragraph!!!!',
},
],
[
{
title: 'some title!!!',
paragraph: 'some para!!',
},
],
],
removed: false,
status: 'NEW',
},
{
authorId: '621f97562511255efa0f135e',
description: [
[
{
title: 'EEEE',
paragraph: 'aaaa',
},
],
],
removed: false,
status: 'NEW',
},
{
description: [
[
{
title: 'TEST',
paragraph: 'TESR',
},
],
],
removed: false,
status: 'NEW',
},
{
authorId: '621f97432511255efa0f135c',
description: [
[
{
title: 'My task',
paragraph: 'Parapgraph 19',
},
],
],
removed: false,
status: 'NEW',
},
]
my expected results is something like that, based on search input, which is conts searchInput
const searchInput = "par"
const array = [
{
authorId: '62222a1200142',
description: [
[
{
title: 'English description',
paragraph: 'And english paragraph!!!!',
},
],
[
{
title: 'some title!!!',
paragraph: 'some para!!',
},
],
],
removed: false,
status: 'NEW',
},
{
authorId: '6a000142',
description: [
[
{
title: 'TEST',
paragraph: 'paragraph one',
},
],
],
removed: false,
status: 'NEW',
},
]
Ive already try something like this:
const results = array?.map((el) => el?.description.map((i) => i.map((item) => item.paragraph))).filter((description) =>description.toString().toLowerCase().includes(searchValue))
But it return only arrays with paragraphs and i expected to filter whole objects, with all data, not only strings
You need to put the map inside the filter, so your data isn't modified. You use first map the description paragraphs into an array and check if all the values of the paragraphs includes the searched param.
const searchValue = 'para';
const array = [{authorId: '62222a1cea00a0601f200142',description: [[{title: 'English description',paragraph: 'And english paragraph!!!!',},],[{title: 'some title!!!',paragraph: 'some para!!',},],],removed: false,status: 'NEW',},{authorId: '621f97562511255efa0f135e',description: [[{title: 'EEEE',paragraph: 'aaaa',},],],removed: false,status: 'NEW',},{description: [[{title: 'TEST',paragraph: 'TESR',},],],removed: false,status: 'NEW',},{authorId: '621f97432511255efa0f135c',description: [[{title: 'My task',paragraph: 'Parapgraph 19',},],],removed: false,status: 'NEW',},];
const results = array.filter(el => {
return el.description
.map(i => {
return i.map(item => item.paragraph)
})
.every((description) => {
return description.toString().toLowerCase().includes(searchValue)
})
});
console.log(results);
I didn't tested the function, however it should work. Put the questionmarks when needed.
I think the filter should be on the array itself, since that is what you expect to be the result.
I used the some function to resolve the arrays in the object.
array?.filter(el=>el?.description.some(el2=>el2.some(el3=>el3?.paragraph.toString().toLowerCase().includes(searchValue))))
It's easier to understand if you don't have everything on one line.
Use filter to return a new array of objects where the description (inner) array has an at leasr one object that contains a paragraph containing the query.
const array=[{authorId:"62222a1cea00a0601f200142",description:[[{title:"English description",paragraph:"And english paragraph!!!!"}],[{title:"some title!!!",paragraph:"some para!!"}]],removed:!1,status:"NEW"},{authorId:"621f97562511255efa0f135e",description:[[{title:"EEEE",paragraph:"aaaa"}]],removed:!1,status:"NEW"},{description:[[{title:"TEST",paragraph:"TESR"}]],removed:!1,status:"NEW"},{authorId:"621f97432511255efa0f135c",description:[[{title:"My task",paragraph:"Parapgraph 19"}]],removed:!1,status:"NEW"}];
const query = 'par';
const out = array.filter(outer => {
// Return an object when the inner array
// of the function has some object that
// contains a paragraph containing the query
return outer.description.some(arr => {
return arr.some(inner => {
return inner.paragraph
.toLowerCase()
.includes(query);
});
});
});
console.log(out);
Additional documentation
some
have you tried .reduce()? It is combination of .map() and .filter() as you can see here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
Takes some time to get into .reduce(), but once you get it, you will like it!

Why can't I access props within my component?

I've copied the Grid Component Example into a single-file component (Grid.vue). Within that component, I'm not able to access the columns prop. console.log(this.columns) always prints: [__ob__: Observer] to the log. Can someone tell me why? This works fine in their example on the page and in JSFiddle.
Here's my Grid.vue file:
<script>
export default {
name: 'grid',
props: {
data: Array,
columns: Array,
filterKey: String
},
data: function() {
var sortOrders = {}
console.log(this.columns)
this.columns.forEach((column) => {
sortOrders[column] = 1
});
return {
sortCol: '',
sortOrders: sortOrders
}
},
computed: {
filteredData: function () {
var sortCol = this.sortCol
var filterKey = this.filterKey && this.filterKey.toLowerCase()
var order = this.sortOrders[sortCol] || 1
var data = this.data
if (filterKey) {
data = data.filter((row) => {
return Object.keys(row).some((key) => {
return String(row[key]).toLowerCase().indexOf(filterKey) > -1
})
})
}
if (sortCol) {
data = data.slice().sort((a, b) => {
a = a[sortCol]
b = b[sortCol]
return (a === b ? 0 : a > b ? 1 : -1) * order
})
}
return data
}
},
filters: {
capitalize: function (str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
},
methods: {
sortBy: function (key) {
this.sortCol = key
console.log(this.sortOrders[key])
this.sortOrders[key] = this.sortOrders[key] * -1
console.log(this.sortOrders[key])
}
},
created() {
},
mounted() {
// var app = this
},
}
</script>
I'm using this component within another component like so:
<template>
<div>
<form id="search">
Search <input name="query" v-model="searchQuery">
</form>
<grid :data="things" :columns="thingColumns" :filterKey="searchQuery"></grid>
</div>
</template>
<script>
import Grid from './Grid.vue';
export default {
name: 'things-grid',
data: function() {
return {
things: [],
thingColumns: [],
searchQuery: ''
}
},
mounted() {
var app = this
app.things = [
{id: 1, this: 'this 1', that: 'that 1', thing: 'thing 1'},
{id: 2, this: 'this 2', that: 'that 2', thing: 'thing 2'},
{id: 3, this: 'this 3', that: 'that 3', thing: 'thing 3'},
{id: 4, this: 'this 4', that: 'that 4', thing: 'thing 4'},
{id: 5, this: 'this 5', that: 'that 5', thing: 'thing 5'},
]
app.thingColumns = [
'this', 'that', 'thing'
]
app.searchQuery = ''
},
components: { Grid }
}
</script>
In:
<grid :data="things" :columns="thingColumns" :filterKey="searchQuery"></grid>
The value of this.thingColumns is passed as :columns when mounting.
Thus, the console.log(this.columns) inside Grid.vue/data() prints when it is mounting.
And when it is mounting, thingColumns is empty in the parent:
data: function() {
return {
things: [],
thingColumns: [], // initially empty
searchQuery: ''
}
},
mounted() {
var app = this
// ...
app.thingColumns = [ // this code only runs after it is mounted
'this', 'that', 'thing'
]
// ...
},
Since the console.log(this.columns) inside Grid.vue/data() prints when it is mounting, that is, before it is mounted, it prints an empty array:
[__ob__: Observer] // this is an empty array, the __ob__ thing is related to Vue internals
Because, well, parent's thingColumns will only have data after the mounted() hook executes.
And since it is a reactive array, when you update it, it will update the child grid component as well.
Solution:
Move the property initalization code from mounted() to created():
created() { // was mounted()
var app = this
// ...
app.thingColumns = [
'this', 'that', 'thing'
]
// ...
},
This will initialize the data sooner and make it available in time for the console.log() in the child to pick it up.

Ramda.js Combine two array of objects that share the same property ID

I have these two array of objects
todos: [
{
id: 1,
name: 'customerReport',
label: 'Report send to customer'
},
{
id: 2,
name: 'handover',
label: 'Handover (in CRM)'
},
]
And:
todosMoreDetails: [
{
id: 1,
checked: false,
link: {
type: 'url',
content: 'http://something.com'
},
notes: []
},
{
id: 2,
checked: false,
link: {
type: 'url',
content: 'http://something.com'
},
notes: []
}
]
So that the final array of objects will be a combination of the two, based on the object ID, like below:
FinalTodos: [
{
id: 1,
checked: false,
link: {
type: 'url',
content: 'http://something.com'
},
notes: [],
name: 'customerReport',
label: 'Report send to customer'
},
{
id: 2,
checked: false,
link: {
type: 'url',
content: 'http://something.com'
},
notes: [],
name: 'handover',
label: 'Handover (in CRM)'
}
]
I tried with merge mergeAll and mergeWithKey but I am probably missing something
You can achieve this with an intermediate groupBy:
Transform the todosMoreDetails array into an object keyed by todo property ID using groupBy:
var moreDetailsById = R.groupBy(R.prop('id'), todosMoreDetails);
moreDetailsById is an object where the key is id, and the value is an array of todos. If the id is unique, this will be a singleton array:
{
1: [{
id: 1,
checked: false,
link: {
type: 'url',
content: 'http://something.com'
},
notes: []
}]
}
Now transform the todos array by merging each todo to it's details you retrieve from the grouped view:
var finalTodos = R.map(todo => R.merge(todo, moreDetailsById[todo.id][0]), todos);
An alternate more detailed way:
function mergeTodo(todo) {
var details = moreDetailsById[todo.id][0]; // this is not null safe
var finalTodo = R.merge(todo, details);
return finalTodo;
}
var moreDetailsById = R.groupBy(R.prop('id'), todosMoreDetails);
var finalTodos = todos.map(mergeTodo);
I guess merge is only used for arrays. Have a search for object "extend". Maybe storing the todo details not in seperate objects is the better solution.
Using jQuery? https://api.jquery.com/jquery.extend/
Using underscore? http://underscorejs.org/#extend
Native approach? https://gomakethings.com/vanilla-javascript-version-of-jquery-extend/
Using underscore:
var result = [];
var entry = {};
_.each(todos, function(todo) {
_.each(todosMoreDetails, function(detail) {
if (todo.id == detail.id) {
entry = _.extend(todo, detail);
result.push(entry);
}
}
});
return result;

Ext js find records in store with same atribute

How can I browse a store and find the number of records which have one attribute the same?
I have tired filterBy but there you can only enter a concrete value not an attribute
Let's say I have this records:
record1{
name: 'John'
}
record2{
name'John'
}
record3{
name:'Steve'
}
Return the records with the same name
Just loop over the collection:
var seen = {};
store.each(function(rec) {
var name = rec.get('name');
if (!seen.hasOwnProperty(name)) {
seen[name] = 0;
}
++seen[name];
});
You might also be interested by Grouping:
var myStore = Ext.create('Ext.data.Store', {
groupField: 'name',
groupDir : 'DESC'
});
myStore.getGroups(); // returns:
[
{
name: 'yellow',
children: [{
name: 'John'
}, {
name: 'John'
}]
},
{
name: 'Steve',
children: [{
name: 'Steve'
}]
}
]
Then you can count how many children there is in each group.
(More details here: http://docs.sencha.com/extjs/4.2.0/#!/api/Ext.data.Store)

Categories