Related
Say I have the following array:
let arr = [{a: 1, b: 2}, {a: 2, b: 4}, {a: 8, b: -1}]
I would like to compute the cumulative sum of each key, but I would also like the output to be an array of the same length with the cumulative values at each step. The final result should be:
[{a: 1, b: 2}, {a: 3, b: 6}, {a: 11, b: 5}]
My issue is that I am not able to obtain the array as desired. I only get the final object with this:
let result = arr.reduce((accumulator, element) => {
if(accumulator.length === 0) {
accumulator = element
} else {
for(let i in element){
accumulator[i] = accumulator[i] + element[i]
}
}
return accumulator
}, [])
console.log(result); // {a: 11, b: 5}
What you're after sounds like the scan() higher-order function (borrowing the idea from ramda.js), which allows you to return an accumulated result for each element within your array. The scan method is similar to how the .reduce() method behaves, except that it returns the accumulator for each element. You can build the scan() function yourself like so:
let arr = [{a: 1, b: 2}, {a: 2, b: 4}, {a: 8, b: -1}];
const scan = ([x, ...xs], fn) => xs.reduce((acc, elem) => {
return [...acc, fn(acc.at(-1), elem)];
}, xs.length ? [x] : []);
const res = scan(arr, (x, y) => ({a: x.a+y.a, b: x.b+y.b}));
console.log(res);
You might consider further improvements such as providing an initial value to the scan method (similar to how reduce accepts one). Also, if you need better browser support the .at() method currently has limited browser support, so you may instead consider creating your own at() function:
const at = (arr, idx) => idx >= 0 ? arr[idx] : arr[arr.length + idx];
You can easily achieve the result using reduce as
let arr = [
{ a: 1, b: 2 },
{ a: 2, b: 4 },
{ a: 8, b: -1 },
];
const result = arr.reduce((acc, curr, i) => {
if (i === 0) acc.push(curr);
else {
const last = acc[i - 1];
const newObj = {};
Object.keys(curr).forEach((k) => (newObj[k] = curr[k] + last[k]));
acc.push(newObj);
}
return acc;
}, []);
console.log(result);
Something like this:
const arr = [{a: 1, b: 2}, {a: 2, b: 4}, {a: 8, b: -1}]
const result = arr.reduce((accumulator, element, index) => {
if(accumulator.length === 0) {
accumulator.push(element)
} else {
const sum = {};
for(let i in element) {
sum[i] = element[i] + (accumulator[index - 1][i] || 0)
}
accumulator.push(sum)
}
return accumulator
}, [])
console.log(result);
Another option is keep sum result using a Map, it helps if keys in elements of the array are not always same.
const arr = [{a: 1, b: 2}, {a: 2}, {a: 8, b: -1}];
const map = new Map();
const result = arr.map((element) => {
const sum = {};
for (let i in element) {
sum[i]= element[i] + (map.get(i) || 0);
map.set(i, sum[i]);
}
return sum;
});
console.log(result);
Here is a bit more concise reduce, probably not as readable as a consequence...
array.reduce((y,x,i) => ( i===0 ? y : [...y, {a: x.a + y[i-1].a, b: x.b + y[i-1].b}]),[array[0]])
let array = [{a: 1, b: 2}, {a: 2, b: 4}, {a: 8, b: -1}]
let culm = array.reduce((y,x,i) => ( i===0 ? y : [...y, {a: x.a + y[i-1].a, b: x.b + y[i-1].b}]),[array[0]])
console.log(culm)
Given:
const xs =
[ {a: 1, b: 2}
, {a: 2, b: 4}
, {a: 8, b: -1}];
Define a function sum such as:
const sum = ([head, ...tail]) =>
tail.reduce((x, y) =>
({a: (x.a+y.a), b: (x.b+y.b)}), head);
sum(xs);
//=> {a: 11, b: 5}
Then apply that function in a map on larger slices of xs:
xs.map((_, i, arr) => sum(arr.slice(0, i+1)));
//=> [ {a: 1, b: 2}
//=> , {a: 3, b: 6}
//=> , {a: 11, b: 5}]
As always I will explain my problem by example (that I solved but its a lot of code and its ugly, that's why I'm looking for a better solution). I'm trying to look at an object like this:
const object1 = {
a: {a:1},
b: 2,
c: 3,
d: 4,
};
I want to check if this object has any of the following properties [a,f] and if have one of them to create a new object with these properties
const object2 = {
a: {a:1},
};
const object1 = {
a: {a:1},
b: 2,
c: 3,
d: 4,
}
const arrOfItem = ['a', 'd']
const newObj = {}
for(let item in object1) {
if(arrOfItem.includes(item)) {
newObj[item]= object1[item]
}
}
console.log(newObj)
see if this works for you,
function makeObject (properties) {
const originalObject = {
a: {a:1},
b: 2,
c: 3,
d: 4,
};
let newObject = {}
properties.forEach(property => {
if(originalObject.hasOwnProperty(property)) {
newObject[property] = originalObject[property];
}
});
return newObject;
}
pass the properties as an array of strings to makeObject function
const d = ['a', 'f', 'd']
const object1 = {
a: {a:1},
b: 2,
c: 3,
d: 4,
};
const object2 = d.reduce((acc, ele) => {
if(object1[ele] !== undefined) acc[ele] = object1[ele];
return acc;
}, {});
console.log(object2);
I have two objects:
let first = {
a: 'John',
b: 22,
c: 'example'
}
let second = {
b: 55,
d: 'demo'
}
I want to replace only already existing items from second object to first one. Result should look like this (so only b item should be changed, and new d item ignored):
{
a: 'John',
b: 55, // changed
c: 'example'
}
Merge will not work because it will add also new item.
I can use foreach but I believe that there should be shorted answer for this. I'm already using lodash in my project so I can use function from there, but I cannot find any for this purpose. Is there any?
With lodash you could do something like this with _.merge, _.pick and _.keys:
let first = {
a: 'John',
b: 22,
c: 'example'
}, second = {
b: 55,
d: 'demo'
}
let result = _.merge(first, _.pick(second, _.keys(first)))
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
With ES6 you can use Object.keys and then Array.forEach on a new object like this:
let first = {
a: 'John',
b: 22,
c: 'example'
}, second = {
b: 55,
d: 'demo'
}, result = new Object(null)
Object.keys(first).forEach(k => result[k] = second[k] || first[k])
console.log(result)
This assumes you do not want to mutate any of the objects. IF you do not care:
let first = {
a: 'John',
b: 22,
c: 'example'
}, second = {
b: 55,
d: 'demo'
}
Object.keys(first).forEach(k => first[k] = second[k] || first[k])
console.log(first)
You could use this if you use ES6
let merge = { ...first, ..._.pick(second, Object.keys(first)) }
You can use a loop over the keys in the second array. For any keys that exist in the first, overwrite the value.
let first = {
a: 'John',
b: 22,
c: 'example'
}
let second = {
b: 55,
d: 'demo'
}
for (const k in second) {
if (k in first) {
first[k] = second[k];
}
}
console.log(first);
You want to update the values of the
intersection
of the properties.
So basically something like:
Object.keys(a).forEach(prop => if(b.hasOwnProperty(prop)) a[prop] = b[prop]))
Or
_.intersection(Object.keys(a), Object.keys(b)).forEach(prop => a[prop] = b[prop])
let first = {
a: 'John',
b: 22,
c: 'example'
}
let second = {
b: 55,
d: 'demo'
}
Object.keys(second).forEach(function(key,index){
if (first.hasOwnProperty(key)) {
first[key]=second[key];
}
});
console.log(first);
With Lodash two functions MergeWith and Pick
var a = {
a: 'John',
b: 22,
f:[11,22,2],
d: {a:1,b:2,c:0},
c: 'example'
}
var b = {
b: 55,
f:[3],
d: {c:1,b:11},
}
function mergeObjects(a,b){
let common = _.pick(b, Object.keys(a));
return _.mergeWith(a, common, customizer)
}
function customizer(objValue, srcValue) {
if (_.isArray(objValue)) {
return objValue.concat(srcValue);
}
if(_.isObject(objValue)) {
return mergeObjects(objValue,srcValue)
}
}
mergeObjects(a,b)
JavaScript findIndex returns the very first finded index in case of duplicate values
const arr = [{a: 10, b: 20, c: 30},{a: 15, b: 25, c: 32},{a: 10, b: 23, c: 350}]
const index = arr.findIndex(m => m.a === 10)
console.log(index);
The above code will only return 0 index.
What should I do to get index 2 as well.
You could filter the keys of the array like this:
const arr = [{a: 10, b: 20, c: 30},{a: 15, b: 25, c: 32},{a: 10, b: 23, c: 350}]
const indices = [...arr.keys()].filter(i => arr[i].a === 10)
console.log(indices)
Or, just use a for loop
const arr = [{a: 10, b: 20, c: 30},{a: 15, b: 25, c: 32},{a: 10, b: 23, c: 350}]
const output = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i].a === 10)
output.push(i)
}
console.log(output)
arr.reduce((result, current, index) => {
return result.concat(current.a == 10 ? [index]: [])
})
You can use of Array.reduce to loop on your arr and returns every indexes
Single line :
const arr = [{
a: 10,
b: 20,
c: 30,
}, {
a: 15,
b: 25,
c: 32,
}, {
a: 10,
b: 23,
c: 350,
}]
// Value to check for
const v = 10;
// we loop on every entry of the arr
// we start with an empty array and look if the entries
// contains the value 10, if they do, we push the index of the
// entry in the array that was empty
//
// we do that for every entry of arr, when we treat all, we return
// the list of matching entries
const idx = arr.reduce((tmp, x, xi) => (x.a === v ? [...tmp, xi] : tmp), []);
console.log(idx);
Explained :
const arr = [{
a: 10,
b: 20,
c: 30,
}, {
a: 15,
b: 25,
c: 32,
}, {
a: 10,
b: 23,
c: 350,
}]
const valToCheck = 10;
const indexes = arr.reduce((tmp, x, xi) => {
if (x.a === valToCheck) {
tmp.push(xi);
}
return tmp;
}, []);
console.log(indexes);
Use map and filter:
const arr = [{a: 10, b: 20, c: 30},{a: 15, b: 25, c: 32},{a: 10, b: 23, c: 350}];
const res = arr.map(({ a }, i) => a == 10 ? i : "").filter(String);
console.log(res);
You could map the indices of the found items and filter only valid indices.
const
array = [{ a: 10, b: 20, c: 30 }, { a: 15, b: 25, c: 32 }, { a: 10, b: 23, c: 350 }],
indices = array
.map((m, i) => m.a === 10 ? i : -1)
.filter(i => i != -1);
console.log(indices);
You can just iterate through array and push matching index in new array.
const arr = [{a: 10, b: 20, c: 30},{a: 15, b: 25, c: 32},{a: 10, b: 23, c: 350}]
let p = []
arr.forEach((ele,index)=> ele.a===10 ? p.push(index) : '')
console.log(p)
You can try like this:
const arr = [{a: 10, b: 20, c: 30},{a: 15, b: 25, c: 32},{a: 10, b: 23, c: 350}];
const indices = arr.map((x, index) => {
if (x.a === 10) {
return index;
}
})
.filter(x => x !== undefined);
console.log(indices);
If you want a function which works directly with Array, add this in Array.prototype
Array.prototype.findAllIndexes = function(iteratee){
var resultArray = [];
this.forEach(function(element) {
var validated = iteratee.call(this, element);
if(validated) {
resultArray.push(element);
}
});
return resultArray;
}
Then you can use it anywher you want with array object
(please handle your corner cases based od data type you use)
const arr = [{a: 10, b: 20, c: 30},{a: 15, b: 25, c: 32},{a: 10, b: 23, c: 350}]
const index = arr.findAllIndexes(m => m.a === 10)
console.log(index);
You can use some helper libraries to this kind of stuff like lodash.js, you can include them in your project.
How to fully copy / overwrite an object using Object.assign()?
If there would be another approach, it would be highly appreciated. :)
The purpose is to update the existing object to its new value.
Below is my code snippet and the expected result should be the same as the value of object2 only.
Expected result: { a: 8, b: 7 }
const object1 = {
a: 1,
b: 2,
c: {
d: 3
}
};
const object2 = {
a: 8,
b: 7
};
Object.assign(object1, object2);
console.log(object1);
For keeping the same object reference, you could remove all properties in advance and then assign the wanted properties with Object.assign.
const object1 = { a: 1, b: 2, c: { d: 3 } };
const object2 = { a: 8, b: 7 };
Object.assign(
Object.keys(object1).reduce((o, k) => (Reflect.deleteProperty(o, k), o), object1),
object2
);
console.log(object1);
IE
var object1 = { a: 1, b: 2, c: { d: 3 } },
object2 = { a: 8, b: 7 };
Object.keys(object2).reduce(
function (object, key) {
object[key] = object2[key];
return object;
},
Object.keys(object1).reduce(function (object, key) {
delete object[key];
return object;
}, object1)
);
console.log(object1);
If you want to override the object1 properties with those from object2, you need to do it like this:
object1 = Object.assign({}, object2);
But with this you need to declare object1 with var keyword so you can assign a new value to it.
Demo:
var object1 = {
a: 1,
b: 2,
c: {
d: 3
}
};
const object2 = {
a: 8,
b: 7
};
object1 = Object.assign({}, object2);
console.log(object1);