Related
I'm working with Javascript, and have the following array of objects:
const offersStep = [
{a: 1, price: 67.10},
{a: 3, price: 88.20},
{a: 5, price: 88.20},
{a: 7, price: 57.10},
{a: 13, price: 57.10},
{a: 15, price: 57.10},
{a: 29, price: 57.10},
{a: 30, price: 57.10},
]
What and How i have to do, in order to store in a new array, those objects, ignoring it those who the prop price has a value who is already stored on the new array?
Expected output:
offers = [
{a: 1, price: 67.10},
{a: 3, price: 88.20},
{a: 7, price: 57.10}
]
Use iteration to determine if previously saved prices match the current object being iterated.
const offersStep = [
{a: 1, price: 67.10},
{a: 3, price: 88.20},
{a: 5, price: 88.20},
{a: 7, price: 57.10},
{a: 13, price: 57.10},
{a: 15, price: 57.10},
{a: 29, price: 57.10},
{a: 30, price: 57.10},
]
// Vars to store previously recorded prices.
var storedPrices = [];
// Var to store unique offersStep.
var uniqueOffersStep = [];
// Iterates through each of the offersStep array objects.
offersStep.forEach (function(value) {
// Sets a boolean to false .. used later to determine if the current object is added to the unique array.
var isSkip = false;
// Iterates through each of the storedPrices array.
storedPrices.forEach (function(spvalue) {
// If the current objects price matches one of the storedPrices then it sets the skip flag to true.
if (value.price == spvalue) {
isSkip = true;
}
})
// Adds the current objects price to the storedPrices array.
storedPrices.push(value.price);
// Check if the skip flag is still false after checking stored prices and adds the object to the uniqueOffers variable.
if (isSkip == false) {
uniqueOffersStep.push(value);
}
})
// Outputs the final var to console.
console.log(uniqueOffersStep);
As this relies on multiple layers of iteration it would be fairly resource heavy with larger data sets.
Hope this helps.
You can store the objects in a Map keyed off its price. After you have stored unique prices, spread the values() iterator of the Map into a new array to get your result.
const offersStep = [
{a: 1, price: 67.10},
{a: 3, price: 88.20},
{a: 5, price: 88.20},
{a: 7, price: 57.10},
{a: 13, price: 57.10},
{a: 15, price: 57.10},
{a: 29, price: 57.10},
{a: 30, price: 57.10},
];
function filterOffers(offers) {
let offersByPrice = new Map();
for (let offer of offers) {
if (!offersByPrice.has(offer.price)) {
// Store the offer
offersByPrice.set(offer.price, offer);
}
}
return [...offersByPrice.values()];
}
console.log(filterOffers(offersStep));
Idea
Filter the original array, keeping tabs on the prices encountered so far by means of a set.
Code
const offersStep = [
{a: 1, price: 67.10},
{a: 3, price: 88.20},
{a: 5, price: 88.20},
{a: 7, price: 57.10},
{a: 13, price: 57.10},
{a: 15, price: 57.10},
{a: 29, price: 57.10},
{a: 30, price: 57.10}
];
let offer
, nset_prices = new Set()
;
offer = offersStep.filter ( po_item => {
let b_ok = !nset_prices.has(po_item.price)
;
if (b_ok) {
nset_prices.add(po_item.price)
}
return b_ok;
});
console.log(offer);
Let me if the code works for you!
const offersStep = [
{ a: 1, price: 67.1 },
{ a: 3, price: 88.2 },
{ a: 5, price: 88.2 },
{ a: 7, price: 57.1 },
{ a: 13, price: 57.1 },
{ a: 15, price: 57.1 },
{ a: 29, price: 57.1 },
{ a: 30, price: 57.1 },
];
let tempOffers = {};
offersStep.forEach((eachObj) => {
const { price } = eachObj;
if (!tempOffers[price]) {
tempOffers[price] = eachObj;
}
});
const offers = Object.values(tempOffers);
console.log("offers are", offers);
const newArray = offersStep.reduce((acc,curr) => {
if(acc.find(item => item.a === curr.a)) {
return acc
}
return acc.concat(curr)
},[])
You could filter with a set of prices.
const
filterWithSet = s => ({ price }) => !s.has(price) && s.add(price),
offersStep = [{ a: 1, price: 67.10 }, { a: 3, price: 88.20 }, { a: 5, price: 88.20 }, { a: 7, price: 57.10 }, { a: 13, price: 57.10 }, { a: 15, price: 57.10 }, { a: 29, price: 57.10 }, { a: 30, price: 57.10 }],
result = offersStep.filter(filterWithSet(new Set));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Using reduce, find, and ternary operator, to iterate given array, check if item price is existing, and append to a collection array if not found:
const offersStep = [
{a: 1, price: 67.10},
{a: 3, price: 88.20},
{a: 5, price: 88.20},
{a: 7, price: 57.10},
{a: 13, price: 57.10},
{a: 15, price: 57.10},
{a: 29, price: 57.10},
{a: 30, price: 57.10},
]
const offers = offersStep.reduce(
(arr, item) => // collection array, and iteration item
// check collection array for item price
(arr.find(item2 => item2.price === item.price))
? arr // item price exists so return array unchanged
: [...arr, item], // return array with item appended
[] // initial collection array
);
console.log(offers);
Using Array.Some
// data source
const offersStep = [
{a: 1, price: 67.10},
{a: 3, price: 88.20},
{a: 5, price: 88.20},
{a: 7, price: 57.10},
{a: 13, price: 57.10},
{a: 15, price: 57.10},
{a: 29, price: 57.10},
{a: 30, price: 57.10},
];
// this is the array to store new objects after dupliacte check
const offers =[];
offersStep.forEach(
(d,i)=>{
//push the first object from the array of objects
(i==0)?offers.push({a:d.a,price:d.price}):
//for the subsequent index, check offers if there exists at least one
// value for price that matches the current price from offersStep.price
// if yes, don't push, else push
offers.some(x=>x.price==d.price)===false?offers.push({a:d.a,price:d.price}):null
}
);
console.log(offers);
i have an array of objects like below
[
{value: 1, id: 1, name: "x"},
{value: 5, id: 1, name: "x"},
{value: 1, id: 1, name: "y"},
{value: 8, id: 1, name: "y"},
{value: 1, id: 2, name: "x"},
{value: 3, id: 2, name: "x"},
{value: 1, id: 2, name: "y"},
{value: 4, id: 2, name: "y"}
]
i want to guet the object with max value with the same "name" and "id"
and push it in a new array ,
the expected output is like this :
[
{value: 5, id: 1, name: "x"},
{value: 8, id: 1, name: "y"},
{value: 3, id: 2, name: "x"},
{value: 4, id: 2, name: "y"},
]
thank you
You can use reduce method to do this,
const data = [
{value: 1, id: 1, name: "x"},
{value: 5, id: 1, name: "x"},
{value: 1, id: 1, name: "y"},
{value: 8, id: 1, name: "y"},
{value: 1, id: 2, name: "x"},
{value: 3, id: 2, name: "x"},
{value: 1, id: 2, name: "y"},
{value: 4, id: 2, name: "y"}
]
const res = data.reduce((prev, curr) => {
const index = prev.findIndex((item) => item.id === curr.id && item.name === curr.name);
if(index > -1) {
const obj = prev[index];
if(obj.value < curr.value) {
prev[index] = {...obj, value: curr.value};
return prev;
}
}
prev.push(curr);
return prev;
}, []);
console.log(res);
Using Array.prototype.reduce, you can group that array using id_name key pair and store the maximum values as follows.
const input = [
{value: 1, id: 1, name: "x"},
{value: 5, id: 1, name: "x"},
{value: 1, id: 1, name: "y"},
{value: 8, id: 1, name: "y"},
{value: 1, id: 2, name: "x"},
{value: 3, id: 2, name: "x"},
{value: 1, id: 2, name: "y"},
{value: 4, id: 2, name: "y"}
];
const groupBy = input.reduce((acc, cur) => {
const key = `${cur.id}_${cur.name}`;
if (!acc[key]) {
acc[key] = cur;
}
if (acc[key].value < cur.value) {
acc[key].value = cur.value;
}
return acc;
}, {});
const output = Object.values(groupBy);
console.log(output);
Reduce is used to return a new value that is basically accumulator (adds on previous value) from all the items in the array. Here we can use it to group items using specific key. As you wrote you want to have items showing a record with biggest value having same id and name, these values can be taken as a key (lets look at them as composite private keys of this object).
On each iteration, we check if there is already an object with that key added to the list, if it wasn't we add the object we are now on (during iteration) or if it was already added if its value is smaller than the current object we are on. If the value is smaller, we override the object with the current one.
In the end, we use JS Object.values method that strips away the keys and returns only the values of the object.
const list = [
{value: 1, id: 1, name: "x"},
{value: 5, id: 1, name: "x"},
{value: 1, id: 1, name: "y"},
{value: 8, id: 1, name: "y"},
{value: 1, id: 2, name: "x"},
{value: 3, id: 2, name: "x"},
{value: 1, id: 2, name: "y"},
{value: 4, id: 2, name: "y"}
];
const groupedResults = list.reduce((result, currentObject) => {
const currentKey = currentObject.id + currentObject.name;
if (!result[currentKey] || result[currentKey].value < currentObject.value) { /* Here we check if object with certain key was assigned to previously or if it was is the value smaller than of the object that we are currently seeing */
result[currentKey] = Object.assign({}, currentObject) //We need to do copy of the object (it can be also done using object destructuring) in order to have a new object that will not be bound by reference with the original one
};
return result;
}, {});
const requestedList = Object.values(groupedResults);
console.log(requestedList)
So simplified code.
var a = [
{ name: "first", num: 1 },
{ name: "first", num: 2 },
{ name: "first", num: 3 },
{ name: "first", num: 4 },
{ name: "first", num: 5 },
{ name: "first", num: 6 },
{ name: "first", num: 7 },
{ name: "first", num: 8 },
{ name: "first", num: 9 }
];
var b = a.filter(function(el) {
return el.num % 2 == 0;
});
console.log("a1", a); // [1, 20, 3, 40, 5, 60, 7, 80, 9]
console.log("b1", b); // [20, 40, 60, 80]
for (let i = 0; i < b.length; i++) {
b[i].num = b[i].num * 10;
}
console.log("a2", a); // [1, 20, 3, 40, 5, 60, 7, 80, 9]
console.log("b2", b); // [20, 40, 60, 80]
My new understanding is the array element contains a reference to an object, not the object. What are some ways to get those objects duplicated?
Filter, then build new objects from the filtered array and put the new things in a new array?
Use some method I'm not currently familiar with?
Redesign the code to stop using objects in an array?
Also, what's up with console.log() showing the variables have changed when placed before the for loop?
If you wish to duplicate the objects inside the array, you should use the map function.
var b = a.filter(val => val.num %2 === 0).map(val => Object.assign({}, val, { num: val.num * 10}));
The map function will return a new array with the value returned from the function. In this example, we are creating a new object Object.assign({}) and duplicating the existing object while changing the num field.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
If you want to clone objects you will need a clone function, I use this function
const clone = obj =>
Array.isArray(obj)
? obj.map(item => clone(item))
: obj instanceof Date
? new Date(obj.getTime())
: obj && typeof obj === 'object'
? Object.getOwnPropertyNames(obj).reduce((o, prop) => {
o[prop] = clone(obj[prop]);
return o;
}, {})
: obj;
You can then clone the array with
let c = clone(b);
Which will be a new array where each object is a new clone.
var a = [{name: 'first', num:1}, {name:'first', num: 2}, {name:'first', num: 3},
{name:'first', num: 4}, {name:'first', num: 5}, {name:'first', num: 6}, {name:'first', num: 7},
{name:'first', num: 8}, {name:'first', num: 9}];
var b = a.filter(function(el){return el.num%2==0 });
const clone = obj =>
Array.isArray(obj)
? obj.map(item => clone(item))
: obj instanceof Date
? new Date(obj.getTime())
: obj && typeof obj === 'object'
? Object.getOwnPropertyNames(obj).reduce((o, prop) => {
o[prop] = clone(obj[prop]);
return o;
}, {})
: obj;
let c = clone(b);
console.log(b[0] === c[0]);
Yes, elements of Array a are all pointers. so you need to use Object.assign (as many says)
and other solution with array reduce usage (see Adrian Brand comment)
var a = [ { name: 'first', num: 1 }
, { name: 'first', num: 2 }
, { name: 'first', num: 3 }
, { name: 'first', num: 4 }
, { name: 'first', num: 5 }
, { name: 'first', num: 6 }
, { name: 'first', num: 7 }
, { name: 'first', num: 8 }
, { name: 'first', num: 9 }
]
var b = a.filter(el=>!(el.num%2)).map(el=>Object.assign({},el))
// other solution with reduce
var c = a.reduce((acc,cur)=>{
if (!(cur.num%2) )acc.push(Object.assign({},cur))
return acc
}, [])
ConsoleArrayNamNum('var a -1-',a) // [1,2,3,4,5,6,7,8,9]
ConsoleArrayNamNum('var b -1-',b) // [2, 4, 6, 8]
ConsoleArrayNamNum('var c -1-',c) // [2, 4, 6, 8]
for(let elm of b)
{ elm.num *= 10 }
ConsoleArrayNamNum('var a -2-',a) // [1,2,3,4,5,6,7,8,9]
ConsoleArrayNamNum('var b -2-',b) // [20, 40, 60, 80]
function ConsoleArrayNamNum(title,arr) {
console.log(title)
for(let elm of arr)
{ console.log(`{ name: '${elm.name}', num: ${elm.num} }`) }
}
.as-console-wrapper { min-height: 100% !important; }
If you want a new array with the final values you can use reduce to do it all in one go, reduce starts with an accumulator of an empty array and each iteration if it meets the condition it adds a clone with the spread operator overriding the num time 10.
var a = [{name: 'first', num:1}, {name:'first', num: 2}, {name:'first', num: 3},
{name:'first', num: 4}, {name:'first', num: 5}, {name:'first', num: 6}, {name:'first', num: 7},
{name:'first', num: 8}, {name:'first', num: 9}];
const evensTimes10 = array => array.reduce((results, item) => {
if (item.num % 2 === 0) {
results.push({ ...item, num: item.num * 10 });
}
return results;
}, []);
var b = evensTimes10(a);
console.log('a1',a); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log('b1',b); // [20, 40, 60, 80]
A simple solution using some ES6 syntax:
var a = [{name: 'first', num:1}, {name:'first', num: 2}, {name:'first', num: 3},
{name:'first', num: 4}, {name:'first', num: 5}, {name:'first', num: 6}, {name:'first', num: 7},
{name:'first', num: 8}, {name:'first', num: 9}];
const b = a
.filter(el => {
if (el.num % 2 === 0) {
return {
...el
}
}
})
.map(newEl => newEl.num * 10);
console.log('a', a); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log('b', b);
.filter() iterates the "a" array and returns only elements with
"num" property that reaches the condition. This is a cloned array.
return { ...el } returns a cloned object thanks to spread
operator.
.map() creates a new array and returns each "el.num" value *
10
Here some info about .map() .filter() and spread operator:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
I found this very interesting site that lists all Javascript functions with their descriptions and shows if is mutable or not, this helps a lot:
https://doesitmutate.xyz/
Is there any method to merge 2 arrays of objects like this
var a = [{id: 1, val: 1},{id: 2, val: 2},{id: 3, val: 3},{id: 4, val: 4},{id: 5, val: 5}];
var b = [{id: 21, val: 21},{id: 22, val: 22},{id: 23, val: 23}]
//final result should be
c = [
{id:1, val: 1},
{id:21, val: 21},
{id:2, val: 2},
{id:22, val: 22},
{id:3, val: 3},
{id:23, val: 23},
{id:4, val: 4},
{id:5, val: 5}
]
offcourse I can create it by myself, but just want to check whether lodash provide it or not
You could first zip the arrays, flatten the result and then use compact to remove the missing array elements (zip adds them as undefined):
var c = _.compact(_.flatten(_.zip(a,b)))
Or using chaining:
var c = _(a)
.zip(b)
.flatten()
.compact()
.value()
var a = [{id: 1, val: 1},{id: 2, val: 2},{id: 3, val: 3},{id: 4, val: 4},{id: 5, val: 5}];
var b = [{id: 21, val: 21},{id: 22, val: 22},{id: 23, val: 23}]
var c = _(a)
.zip(b)
.flatten()
.compact()
.value()
document.getElementById('results').textContent = JSON.stringify(c);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<pre id="results"></pre>
var a = [{id: 1, val: 1},{id: 2, val: 2},{id: 3, val: 3},{id: 4, val: 4},{id: 5, val: 5}];
var b = [{id: 21, val: 21},{id: 22, val: 22},{id: 23, val: 23}];
// loop through the biggest array and reduce the result (no need for the value we just need the accumulator and the index)
var result = _.reduce(a.length < b.length? b: a, function(res, _, i) {
if(i < a.length) res.push(a[i]); // if there is still elements in a, push the current one
if(i < b.length) res.push(b[i]); // if there is still elements in b, push the current one
return res;
}, []);
console.log(result);
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>
In plain Javascript, you could use a function which iterates to the minimum length of both, assembles the values and concat the rest at the end.
function insert(a, b) {
var c = [],
i = 0,
l = Math.min(a.length, b.length);
while (i < l) {
c.push(a[i], b[i]);
i++;
}
return c.concat(a.slice(i), b.slice(i));
}
var a = [{ id: 1, val: 1 }, { id: 2, val: 2 }, { id: 3, val: 3 }, { id: 4, val: 4 }, { id: 5, val: 5 }],
b = [{ id: 21, val: 21 }, { id: 22, val: 22 }, { id: 23, val: 23 }];
console.log(insert(a, b));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Ecmascript5 solution using Math.max()(to find the larger array size) and Array.prototype.push() functions:
var a = [{id: 1, val: 1},{id: 2, val: 2},{id: 3, val: 3},{id: 4, val: 4},{id: 5, val: 5}],
b = [{id: 21, val: 21},{id: 22, val: 22},{id: 23, val: 23}],
maxLen = Math.max(a.length, b.length), aLen = a.length, bLen = b.length,
maxList = aLen > bLen ? a : b;
result = [];
for (var i = 0; i < maxLen; i++) {
(i < aLen && i < bLen) ? result.push(a[i], b[i]) : result.push(maxList[i]);
}
console.log(result);
I have two arrays of objects like this:
var arr1 = [{Id: 1, Name: "Test1"}, {Id: 2, Name: "Test2"}, {Id: 3, Name: "Test3"}, {Id: 4, Name: "Test4"}]
var arr2 = [{Id: 1, Name: "Test1"}, {Id: 3, Name: "Test3"}]
I need to compare the elements of the two arrays by Id and remove the elements from arr1 that are not presented in arr2 ( does not have element with that Id). How can I do this ?
var res = arr1.filter(function(o) {
return arr2.some(function(o2) {
return o.Id === o2.Id;
})
});
shim, shim, shim.
You can use a function that accepts any number of arrays, and returns only the items that are present in all of them.
function compare() {
let arr = [...arguments];
return arr.shift().filter( y =>
arr.every( x => x.some( j => j.Id === y.Id) )
)
}
var arr1 = [{Id: 1, Name: "Test1"}, {Id: 2, Name: "Test2"}, {Id: 3, Name: "Test3"}, {Id: 4, Name: "Test4"}];
var arr2 = [{Id: 1, Name: "Test1"}, {Id: 3, Name: "Test3"}, {Id: 30, Name: "Test3"}];
var arr3 = [{Id: 1, Name: "Test1"}, {Id: 6, Name: "Test3"}, {Id: 30, Name: "Test3"}];
var new_arr = compare(arr1, arr2, arr3);
console.log(new_arr);
function compare() {
let arr = [...arguments]
return arr.shift().filter( y =>
arr.every( x => x.some( j => j.Id === y.Id) )
)
}
Making use of a hash (a Set) will give a performance gain:
var arr1 = [{Id: 1, Name: "Test1"}, {Id: 2, Name: "Test2"},
{Id: 3, Name: "Test3"}, {Id: 4, Name: "Test4"}];
var arr2 = [{Id: 1, Name: "Test1"}, {Id: 3, Name: "Test3"}];
arr1 = arr1.filter(function (el) {
return this.has(el.Id);
}, new Set(arr2.map(el => el.Id)));
console.log(arr1);
A new Set is created that gets the Id values from arr2:
"1","3"
That Set is passed as the thisArg to filter, so that within the filter callback it is available as this.