Update and reposition object in array in Redux reducer - javascript

const state = [
{10: {a: 22, b: 33}},
{12: {a: 20, b: 33}},
{15: {a: 22, b: 34}},
{5: {a: 21, b: 30}},
{9: {a: 29, b: 33}},
]
State is an array of objects like above. When the app updates an object, the object should flow up to the first position.
E.g. let's say that we take the second object above (with the primary key 12), and copy and update it so it looks like this:
{12: {a: 45, b: 33}}
And now we want to insert it to the array with the following result:
const state = [
{12: {a: 45, b: 33}},
{10: {a: 22, b: 33}},
{15: {a: 22, b: 34}},
{5: {a: 21, b: 30}},
{9: {a: 29, b: 33}},
]
I understand how to update an object in an immutable fashion, but I cannot get my head around how to accomplish the above.

You could use something like
// an update function, here it just adds a new key
// to the object
const update = (x) => ({
...x,
hello: "world"
});
// a filter factory
const withId = (id) => (item) => Boolean(item[id]); // item with specific ids
const withoutId = (id) => (item) => !Boolean(item[id]); // others
const state = [
{10: {a: 22, b: 33}},
{12: {a: 20, b: 33}},
{15: {a: 22, b: 34}},
{5: {a: 21, b: 30}},
{9: {a: 29, b: 33}},
];
const id = 5;
const newState = state
.filter(withId(id))
.map(update)
.concat(state.filter(withoutId(id)));
console.log(JSON.stringify(newState, null, 4));
What this does is filtering the state between the items you want to update and the rest, apply the update to the selection and concat the untouched items to them.
Below another example on with the same idea which illustrates that you can perform the update on more than one item :
const markAsDone = (todo) => ({
...todo,
done: true
});
const isInGroup = (group) => (todo) => todo.group === group;
const not = (predicate) => (x) => !predicate(x);
const isWork = isInGroup("work");
const notWork = not(isWork);
const state = [
{
todo: "go shopping",
group: "life"
},
{
todo: "go work",
group: "work",
},
{
todo: "go sleep",
group: "life"
}
];
// get work related todos, mark as done and
// append to todo list
const newState = state
.filter(notWork)
.concat(state
.filter(isWork)
.map(markAsDone));
console.log(JSON.stringify(newState, null, 4));

something like this perhaps?
const state = [
{10: {a: 22, b: 33}},
{12: {a: 20, b: 33}},
{15: {a: 22, b: 34}},
{5: {a: 21, b: 30}},
{9: {a: 29, b: 33}},
]
// find the numerical index of the object with the specified "key"
function findIndexOfKey(array, key){
return array.findIndex(function(el){return key in el});
}
// modify object and move to front
function editObjectAndMoveToFront(array, key, updatedValues){
// find index of object with key, for use in splicing
var index = findIndexOfKey(array, key);
// create the updated version of the specified object
var originalObject = array[index];
var originalObjectValue = originalObject[key];
var editedObject = {};
editedObject[key] = Object.assign({}, originalObjectValue, updatedValues)
// here is the new state, with the updated object at the front
var newArray = [
editedObject,
...array.slice(0, index),
...array.slice(index + 1)
]
return newArray
}
const newState = editObjectAndMoveToFront(state, 12, {a: 45, b: 33})
console.log(newState);

Related

Calculating sum of properties of multiple objects

So I have an array of objects with some varying number of properties (but the property names are known), for example:
let data = [{a: 10, b: 1, c:10},
{a: 17, b: 2, c:16},
{a: 23, b: 3, c:41}]
I need to construct an object that sums up the values in the respective properties, so in this example I'd need to construct an object {a: 50, b: 6, c:67}
I wrote the following function to do this:
calcTotalForDataProps(data, props) {
let summedData = {}
for (const prop of props) {
summedData[prop] = 0;
}
data.forEach((dataObj) => {
for (const prop of props) {
summedData[prop] += dataObj[prop];
}
});
return summedData;
}
And you call it like this:
calcTotalForDataProps(data, ['a', 'b', 'c']);
But I'm wondering if there's a much shorter way to write this with ES6?
You could map the wanted props with their new sums for getting an object as result.
function calcTotalForDataProps(data, props) {
return data.reduce((r, o) => Object
.fromEntries(props.map(k => [k, (r[k] || 0) + o[k]])
), {});
}
const data = [{ a: 10, b: 1, c: 10}, { a: 17, b: 2, c: 16 }, { a: 23, b: 3, c: 41 }]
console.log(calcTotalForDataProps(data, ['a', 'b', 'c']));
There's no need to iterate over the properties initially - you can create the property inside the other loop if it doesn't exist yet.
let data = [{a: 10, b: 1, c:10},
{a: 17, b: 2, c:16},
{a: 23, b: 3, c:41}]
const calcTotalForDataProps = (data, props) => {
const summedData = {};
for (const obj of data) {
for (const [prop, num] of Object.entries(obj)) {
summedData[prop] = (summedData[prop] || 0) + num;
}
}
return summedData;
}
console.log(calcTotalForDataProps(data));

How to convert an array containing arrays of objects to single array witht hose object using Ramda expressions?

I am having a difficult time, there is some bad mapping going on on my code.
I have an array containing array of objects like that :
[
[{a: 1, b: 2},{a: 1, b: 3} ],
[{a: 5, b: 2},{a: 2, b: 5}]
]
And I want to make like that :
[
{a: 1, b: 2},
{a: 1, b: 3},
{a: 5, b: 2},
{a: 2, b: 5}
]
In order to do that, I thought I found the magical solution, make things flat, using flatten function, it was not working ( this problem is just a piece of code in a lot of code ) and I was wondering why, i wasted some time to find that this the problem, it is not the behovior I am expecting, as you can see in the image, the first thing I have is an array containing an array having two objects, with flatten method, I was expecting an array of two objects, but I am getting what you see in the image :
The code I have ttried is this :
const expectedArray = R.flatten(myArrayOfArraysOfObjects);
Full example :
const singleTronconPoints = troncon => {
return troncon.geometri_linestring;
};
console.log('troncons : ');
console.log(troncons);
console.log('map troncons points');
console.log(map(singleTronconPoints, troncons));
console.log('flatten');
console.log(flatten(map(singleTronconPoints, troncons)));
and this is full result :
How can I solve that, is there another magical ( :P ) solution ( method ) to solve the problem ?
Any help would be much appreciated.
Array.prototype.reduce() can also be an option:
const arr =[
[{a: 1, b: 2},{a: 1, b: 3}],
[{a: 5, b: 2},{a: 2, b: 5}]
]
const expectedArray = arr.reduce((acc, array) => {
acc.push(...array);
return acc;
}, []);
You can use array.flat
let a = [
[{
a: 1,
b: 2
}, {
a: 1,
b: 3
}],
[{
a: 5,
b: 2
}, {
a: 2,
b: 5
}]
];
let b = a.flat();
console.log(b)
Alternatively you can use reduce and inside callback use forEach and puch items from the nested array to accumulator array
let a = [
[{
a: 1,
b: 2
}, {
a: 1,
b: 3
}],
[{
a: 5,
b: 2
}, {
a: 2,
b: 5
}]
];
let b = a.reduce((acc, curr) => {
curr.forEach(item => acc.push(item))
return acc;
}, []);
console.log(b)
use reduce() + push which faster flat() method.
refer this for to check performance. : https://jsbench.me/0ikcqa83ck/1
let arr = [
[{a: 1, b: 2},{a: 1, b: 3} ],
[{a: 5, b: 2},{a: 2, b: 5}]
]
console.log(arr.flat())
let flattenArr = arr.reduce((acc, val) => (acc.push(...val),acc), [])
console.log(flattenArr);
Array.flat is the magical solution you are looking for !
var arr = [
[{a: 1, b: 2},{a: 1, b: 3} ],
[{a: 5, b: 2},{a: 2, b: 5}]
]
console.log(arr.flat())

get list of object from array of objects using javascript

It is a simple javascript problem and i am unable to get my head through it
const jsObjects = [
{a: 1, b: 2},
{a: 3, b: 4},
{a: 5, b: 6},
{a: 7, b: 8}
]
let result = jsObjects.find(obj => {
return obj.b === 6
})
console.log(result)
i just want to console the entire list of 'b' rather than find a single variable 'b' which holds value 6
is there any way to do that
You can use Array.filter() instead of Array.find()
const jsObjects = [
{a: 1, b: 2},
{a: 3, b: 4},
{a: 5, b: 6},
{a: 7, b: 8}
]
let result = jsObjects.filter(obj => obj.b === 6)
console.log(result)
UPDATE
If you want to take only one property, then you can use Array.map()
const jsObjects = [
{a: 1, b: 2},
{a: 3, b: 4},
{a: 5, b: 6},
{a: 7, b: 8}
]
let result = jsObjects.map(obj => obj.b)
console.log(result);
If you still want them as objects in an array, you can update Harun's code like this:
let myBs = [];
let result = jsObjects.map(obj => myBs.push({b: obj.b}));
console.log(myBs);```

How to map more than one property from an array of objects [duplicate]

This question already has answers here:
Extract certain properties from all objects in array
(5 answers)
Closed 4 months ago.
I have an array of Object as follows:
var obj = [
{a: 1, b: 5, c: 9},
{a: 2, b: 6, c: 10},
{a: 3, b: 7, c: 11},
{a: 4, b: 8, c: 12}
];
I know about how to get single object using Array.map() like this.
var result = obj.map(x=>x.a)
This will give me following result
[1, 2, 3, 4]
But I want result like follows:
[
{a: 1, b: 5},
{a: 2, b: 6},
{a: 3, b: 7},
{a: 4, b: 8}
]
In short, from an array of objects I want to select only a few fields (more than one).
How can I do that?
You can use .map() with Object Destructuring:
let data = [
{a:1,b:5,c:9}, {a:2,b:6,c:10},
{a:3,b:7,c:11}, {a:4,b:8,c:12}
];
let result = data.map(({ a, b }) => ({a, b}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If, as in your example, you want to exclude a particular property or few, you can use destructuring and use rest properties to create an object with only the properties you want:
var obj = [
{a:1,b:5,c:9},
{a:2,b:6,c:10},
{a:3,b:7,c:11},
{a:4,b:8,c:12}
];
const mapped = obj.map(({ c, ...rest }) => rest);
console.log(mapped);
If you want to include properties, simply extract them from the .map callback:
var obj = [
{a:1,b:5,c:9},
{a:2,b:6,c:10},
{a:3,b:7,c:11},
{a:4,b:8,c:12}
];
const mapped = obj.map(({ a, b }) => ({ a, b }));
console.log(mapped);
Use map():
var data = [
{a:1,b:5,c:9},
{a:2,b:6,c:10},
{a:3,b:7,c:11},
{a:4,b:8,c:12}
];
let modified = data.map(obj => ({a: obj.a, b: obj.b}))
console.log(modified);
Or if you prefer destructuring:
var data = [
{a:1,b:5,c:9},
{a:2,b:6,c:10},
{a:3,b:7,c:11},
{a:4,b:8,c:12}
];
let modified = data.map(({ a, b }) => ({a, b}));
console.log(modified);
var obj = [
{a: 1, b: 5, c: 9},
{a: 2, b: 6, c: 10},
{a: 3, b: 7, c: 11},
{a: 4, b: 8, c: 12}
];
var result = obj.map(e => ({a:e.a , b:e.b}));
console.log(result)
You can return a custom object with required properties using map()
var obj = [{a:1,b:5,c:9},
{a:2,b:6,c:10},
{a:3,b:7,c:11},
{a:4,b:8,c:12}
];
let op = obj.map(e => {
return { a:e.a, b: e.b };
})
console.log(op);
In your solution for producing [1,2,3,4], x.a isn’t some micro-syntax, it’s actually a full-fledged JavaScript expression. So you can just replace it with the JavaScript for creating a new object with the properties you want.
var result = obj.map(x=>{a: x.a, b: x.b});
... almost. The additional complication is that a { after a => is interpreted as the beginning of a function body, not the beginning of an object literal. You can avoid this by just wrapping the object literal in otherwise-noop parenthesis.
var result = obj.map(x=>({a: x.a, b: x.b}));
You can make your own custom function for this, and pass it a set of properties which you want to extract. :
var array = [{a:1,b:5,c:9}, {a:2,b:6,c:10}, {a:3,b:7,c:11}, {a:4,b:8,c:12} ];
function extractProperties(arr, properties){
return arr.map((obj)=> Object.keys(obj).reduce((acc,key)=>{
if(properties.has(key))
acc[key] = obj[key];
return acc;
},{}));
}
let set = new Set(["a","b"]);
let result = extractProperties(array, set);
console.log(result);
set.add("c");
console.log("**************result2**********")
let result2 = extractProperties(array, set);
console.log(result2);

Return an Array of Arrays containing objects that share a common value in a property

Say I have an array of 3 objects like this:
[
{
a: 4,
b: 5,
c: 4
},
{
a: 3,
b: 5,
c: 6
},
{
a: 2,
b: 3,
c: 3
}
]
I would like to return an array of arrays containing the objects that share a common value for the property b. So the resulting array would contain only one array containing 2 objects like this:
[
[
{
a: 4,
b: 5,
c: 4
},
{
a: 3,
b: 5,
c: 6
}
]
]
How would I do this?
You could do this with map and filter
var data = [{"a":4,"b":5,"c":4},{"a":3,"b":5,"c":6},{"a":2,"b":3,"c":3}];
var check = data.map(e => {return e.b});
var result = [data.filter(e => { return check.indexOf(e.b) != check.lastIndexOf(e.b)})];
console.log(result)
To group multiple objects in separate arrays with same b values you can use map and forEach
var data = [{"a":4,"b":5,"c":4},{"a":3,"b":5,"c":6},{"a":2,"b":3,"c":3}, {"a":3,"b":7,"c":6},{"a":2,"b":7,"c":3}], result = [];
var check = data.map(e => {return e.b});
data.forEach(function(e) {
if(check.indexOf(e.b) != check.lastIndexOf(e.b) && !this[e.b]) {
this[e.b] = [];
result.push(this[e.b]);
}
(this[e.b] || []).push(e);
}, {});
console.log(result)
This proposal uses a single loop with Array#forEach but without Array#indexOf.
var array = [{ a: 4, b: 5, c: 4 }, { a: 3, b: 5, c: 6 }, { a: 2, b: 3, c: 3 }],
grouped = [];
array.forEach(function (a) {
this[a.b] = this[a.b] || [];
this[a.b].push(a);
this[a.b].length === 2 && grouped.push(this[a.b]);
}, Object.create(null));
console.log(grouped);
You can create a function that accepts fulfillment criteria and will return as many nested arrays as rules passed.
Let's say you have an array of objects, arr.
var arr = [{a: 1, b: 2}, {a: 3, b: 2}, {a: 3, b: 4}, {a: 1, b: 1}]
And you want to return an array with with nested arrays that fulfill a particular requirement, let's say you want objects with an a:1 and b:2.
You can create a function that loops through your rules and creates a nested array with the objects that fulfill each rule.
For example:
var arr = [{a: 1, b: 2}, {a: 3, b: 2}, {a: 3, b: 4}, {a: 1, b: 1}]
function makeNestedArrays() {
var rules = [].slice.call(arguments);
return rules.reduce(function(acc, fn) {
var nestedArr = [];
arr.forEach(function(obj) {
if (fn(obj)) {
nestedArr.push(obj);
}
});
// only push nested array
// if there are matches
if (nestedArr.length) {
acc.push(nestedArr);
}
return acc;
}, []);
}
var result = makeNestedArrays(
function(obj) { return obj.a === 1; },
function(obj) { return obj.b === 2; }
);
console.log(result);
This allows you to pass as many "rules" as you want, and will create a nested array for each rule so long as there is at least one match.
You could use a Map to group them, this should work with any kind of value (just be sure the equality rules check out):
var arr = [{
a: 4,
b: 5,
c: 4
}, {
a: 3,
b: 5,
c: 6
}, {
a: 2,
b: 3,
c: 3
}];
var result = arr.reduce(function(m, o){
var value = o.b;
if(m.has(value)){
m.get(value).push(o);
} else {
m.set(value, [o]);
}
return m;
}, new Map());
console.log(...(result.values()));
If you'd need to filter out the groups of 1:
var arr = [{
a: 4,
b: 5,
c: 4
}, {
a: 3,
b: 5,
c: 6
}, {
a: 2,
b: 3,
c: 3
}];
var result = arr.reduce(function(m, o){
var value = o.b;
if(m.has(value)){
m.get(value).push(o);
} else {
m.set(value, [o]);
}
return m;
}, new Map());
result = [...result.values()].filter(a => a.length > 1);
console.log(result);

Categories

Resources