Set.add only add in 1 item [duplicate] - javascript

This question already has answers here:
How to add an array of values to a Set
(11 answers)
Closed 3 years ago.
Set only adds 1 copy of an array and I'm not sure why it doesn't keep adding other copies.
The function below takes in an array of trips with the travelers id and another array containing the travelers' ids and names. In this chunk of code,
if(item.type === type){
store.add(...item.travelers);
}
I expected that 123,456 and 789 will be added to the set. However, only 123 and 456 are added. Why doesn't the Set add in the second copy (which would be 456 and 789)?
const travelers = [
{
id: 123,
name: 'John'
},
{
id: 456,
name: 'Raymond'
},
{
id: 789,
name: 'Mary'
},
];
const trip = [
{
type: 'car',
travelers: [123, 456]
},
{
type: 'flight',
travelers: []
},
{
type: 'car',
travelers: [456, 789]
},
];
function determinePeopleForEachTripType(arr, travelers, type){
const result = [];
let store = new Set();
for(let i = 0; i< arr.length; i++){
let item = arr[i];
if(item.type === type){
store.add(...item.travelers);
}
}
store.forEach(eachStore =>{
for(let j = 0; j< travelers.length; j++){
if(eachStore === travelers[j].id){
result.push(travelers[j].name)
}
}
})
return result;
}
determinePeopleForEachTripType(trip, travelers, 'car');
Expected result: Set contains [123, 456, 789]. But actual output is Set contains [123, 456]. What are some possible ways to fix this code?

Set.prototype.add only takes one argument, and that's the one element - you need to iterate through travellers and add each item separately:
item.travelers.forEach(Set.prototype.add, store);

const travelers = [
{
id: 123,
name: 'John'
},
{
id: 456,
name: 'Raymond'
},
{
id: 789,
name: 'Mary'
},
];
const trip = [
{
type: 'car',
travelers: [123, 456]
},
{
type: 'flight',
travelers: []
},
{
type: 'car',
travelers: [456, 789]
},
];
function determinePeopleForEachTripType(arr, travelers, type){
const result = [];
let store = new Set();
for(let i = 0; i< arr.length; i++){
let item = arr[i];
if(item.type === type){
for (let traveler of item.travelers) {
store.add(traveler);
}
}
}
store.forEach(eachStore =>{
for(let j = 0; j< travelers.length; j++){
if(eachStore === travelers[j].id){
result.push(travelers[j].name)
}
}
})
return result;
}
const result = determinePeopleForEachTripType(trip, travelers, 'car');
console.log(result)

Related

Javascript Array and Object

I have two arrays of objects.Where each object has different properties, Like this
let array1=[
{id:121122,name:"Matt Jeff"},
{id:121123,name:"Philip Jeff"},
{id:121124,name:"Paul Jeff"}]
let array2=[
{owner_id:121122,id:1211443,value:18},
{owner_id:121127,id:1211428,value:22}]
How can I check if the owner_id in the array2 is equal to the id in array1 then return the new array like this
let newArray=[
{owner_id:121122,id:1211443,value:18}
]
Where the owner_id in array2 is equal to the id in array1.
If I correctly understand what you need, you could do like this:
let array1 = [{
id: 121122,
name: "Matt Jeff"
}, {
id: 121123,
name: "Philip Jeff"
}, {
id: 121124,
name: "Paul Jeff"
}
]
let array2 = [{
owner_id: 121122,
id: 1211443,
value: 18
}, {
owner_id: 121127,
id: 1211428,
value: 22
}
]
const result = array2.filter(({ owner_id }) => array1.some(({ id }) => id === owner_id));
console.log(result);
You could try with nested for like:
let array1=[
{id:121122,name:"Matt Jeff"},
{id:121123,name:"Philip Jeff"},
{id:121124,name:"Paul Jeff"}]
let array2=[
{owner_id:121122,id:1211443,value:18},
{owner_id:121127,id:1211428,value:22}];
let result = [];
for(let i = 0; i < array1.length; i++) {
for(let j = 0; j < array2.length; j++) {
if (array1[i].id === array2[j].owner_id) {
result.push(array2[j]);
}
}
}
console.log(result)
EFFICIENT WAY: Using Set and filter
O(m) - Iterating on array1 and Storing the id in Set
O(n) - Iterating on the array2 and filtering the result which include O(1) to search in Set;
let array1 = [
{ id: 121122, name: "Matt Jeff" },
{ id: 121123, name: "Philip Jeff" },
{ id: 121124, name: "Paul Jeff" },
];
let array2 = [
{ owner_id: 121122, id: 1211443, value: 18 },
{ owner_id: 121127, id: 1211428, value: 22 },
];
const dict = new Set();
array1.forEach((o) => dict.add(o.id));
const result = array2.filter((o) => dict.has(o.owner_id));
console.log(result);

Most efficient way to attach properties from one object to another if certain key:values match?

Let's say I have two data sources that are fairly large, 3000+ entries each. They might look something like this...
const arOfObj1 = [
{ type: 'Something', properties: { name: 'ABC' } },
{ type: 'Something', properties: { name: 'DEF' } },
{ type: 'Something', properties: { name: 'GHI' } },
...and so on...
];
const arOfObj2 = [
{ name: 'ABC', stats: { age: 1, other: "Something" } },
{ name: 'DEF', stats: { age: 2, isEnrolled: true } },
{ name: 'GHI', stats: { age: 3 } },
...and so on...
];
What would be the most efficient way of finding the name property that matches in each object, and appending the stats{...} (or other properties if they exist) from arOfObj2 to arOfObj1? so I would end up with something like this
const newArOfObj1 = [
{ type: 'Something', properties: { name: 'ABC', stats: { age: 1, other: "Something" } },
{ type: 'Something', properties: { name: 'DEF', stats: { age: 2, isEnrolled: true } },
{ type: 'Something', properties: { name: 'GHI', stats: { age: 3 } },
...and so on...
]
My initial thought was to do something like this...
arOfObj1.forEach(obj1 => {
arOfObj2.forEach(obj2 => {
if (obj1.properties.name === obj2.name) {
obj1.stats = obj2.stats
}
})
});
Just not sure if there's a better way than to loop through arOfObj2 for each entry in arOfObj1
I don't know what the most efficient way is, and I don't really know how the memory works in javascript, but I have a way that works.
const arOfObj1 = [
{ type: 'Something', properties: { name: 'ABC' }},
{ type: 'Something', properties: { name: 'DEF' }},
{ type: 'Something', properties: { name: 'GHI' }}
];
const arOfObj2 = [
{ name: 'ABC', stats: { age: 1, other: "Something" } },
{ name: 'DEF', stats: { age: 2, isEnrolled: true } },
{ name: 'GHI', stats: { age: 3 } }
];
var names = new Map();
for(var i = 0, len = arOfObj2.length; i < len; i++){
var obj = arOfObj2[i];
names.set(obj.name, obj.stats);
}
for(var i = 0, len = arOfObj1.length; i < len; i++){
var properties = arOfObj1[i].properties;
properties.stats = names.get(properties.name);
}
console.log(arOfObj1);
What it does is loop through each object, saving the name and stats as the key and value in a Map. Then it loops through the first array of objects, adding the stats property that it gets from Map.get.
Memory is usually not the issue in the JavaScript applications and 3000+ array of objects is not that big for a modern machine. I'll have to assume that you're looking for a speed increase.
In case I am wrong and you're looking for something that would be more memory efficient then ignore my response and look in to batching with something like js batch to avoid loading up memory all at once and instead spreading the memory load across batches.
Assuming we're looking for a fastest way to complete the task.
Code:
map/find
arOfObj1.map(o => ({
...o,
properties: {
...o.properties,
stats: arOfObj2.find(a => a.name === o.properties.name).stats
}
}));
Simple double for loop
let result = [];
for (let i = 0; i < arOfObj1.length; i++) {
let o = arOfObj1[i];
let stats;
for (let j = 0; j < arOfObj2.length; j++) {
if (stats) break;
let a = arOfObj2[j];
if (a.name === o.properties.name) {
stats = a.stats;
}
}
result.push({
...o, properties: {...o.properties, stats}
});
}
Map set - while I was at it #programmerRaj posted his answer with this solution so look at his response, but I'll include it in my speed test below.
So now we can test these for speed:
Filling both arrays with 5000 items and running all solutions while measuring time.
let arOfObj1 = [];
let arOfObj2 = [];
for (let i = 0; i < 5000; i++) {
arOfObj1.push({type: 'Something', properties: { name: `name${i}` }});
arOfObj2.push({name: `name${i}`, stats: { age: i, other: "Something" }});
}
console.time('map/find');
arOfObj1.map(o => ({
...o,
properties: {
...o.properties,
stats: arOfObj2.find(a => a.name === o.properties.name).stats
}
}));
console.timeEnd('map/find');
console.time('for loop');
let result = [];
for (let i = 0; i < arOfObj1.length; i++) {
let o = arOfObj1[i];
let stats;
for (let j = 0; j < arOfObj2.length; j++) {
if (stats) break;
let a = arOfObj2[j];
if (a.name === o.properties.name) {
stats = a.stats;
}
}
result.push({
...o, properties: {...o.properties, stats}
});
}
console.timeEnd('for loop');
console.time('programmerRaj\'s solution');
var names = new Map();
for(var i = 0, len = arOfObj2.length; i < len; i++){
var obj = arOfObj2[i];
names.set(obj.name, obj.stats);
}
for(var i = 0, len = arOfObj1.length; i < len; i++){
var properties = arOfObj1[i].properties;
properties.stats = names.get(properties.name);
}
console.timeEnd('programmerRaj\'s solution');
Running test with Node lts (12) I get the following results:
map/find: 189.902ms
for loop: 188.912ms
programmerRaj's solution: 3.236ms

Javascript - How to combine all combinations into an array of objects

I have the following array:
[{
name: 'foo',
values: '10,12'
},
{
name: 'bar',
values: 'red,blue'
}]
Using some javascript logic I would like to output the following array:
[{
option1: 10,
option2: 'red'
},
{
option1: 10,
option2: 'blue'
},
{
option1: 12,
option2: 'red'
},
{
option1: 12,
option2: 'blue'
}]
What is the best and correct way to achieve this using javascript?
Lets say your first array is named arr.
var arr = [{
name: 'foo',
values: '10,12'
},
{
name: 'bar',
values: 'red,blue'
}];
var v1 = arr[0].values.split(',');
var v2 = arr[1].values.split(',');
var res = new Array();
for(i in v1){
for(j in v2){
res.push({'option1':v1[i],'option2':v2[j]});
}
}
console.log(res);
Here's an approach that can handle an arbitrary number of objects.
function valuesCrossProduct(input) {
return input.flatMap((current, index, array) => {
let result = [];
let values = current.values.split(',');
for (let v of values) {
for (let i = 0; i < array.length; i++) {
if (i <= index) {
// Skip creating cross products with self (i.e. == index)
// and with previously visited objects (i.e. < index).
continue;
}
let iValues = array[i].values.split(',');
let currentKey = `option${index}`;
let iKey = `option${i}`;
for (let iv of iValues) {
result.push({
[currentKey]: v,
[iKey]: iv,
});
}
}
}
return result;
});
}
let twoElementArray = [{
name: 'foo',
values: '10,12'
},
{
name: 'bar',
values: 'red,blue',
}];
let threeElementArray = [{
name: 'foo',
values: '10,12'
},
{
name: 'bar',
values: 'red,blue',
},
{
name: 'baz',
values: 'wham,bam',
}];
console.log(valuesCrossProduct(twoElementArray));
console.log(valuesCrossProduct(threeElementArray));
Functional for the win.
Note: as it is, this only works for an array of two objects, with any number of values in each, where the first set of values are numbers and the second set are strings, which is what you described above.
const arr = [{
name: 'foo',
values: '10,12'
},
{
name: 'bar',
values: 'red,blue'
}];
const values = arr
.map(o => o.values.split(','))
.reduce((cur, next) => {
return cur.map(c => {
return next.map(n => {
return {
option1: parseInt(c),
option2: n
};
});
}).flat();
});
console.log(values);
If you need generic approach to get possible options from various values.
const options = data => {
let sets = [[]];
data.forEach(({ values }, i) => {
const new_set = [];
values.split(",").forEach(value => {
new_set.push(
Array.from(sets, set => [...set, [`option${i + 1}`, value]])
);
});
sets = new_set.flatMap(set => set);
});
return sets.map(set => Object.fromEntries(set));
};
const data = [
{
name: "foo",
values: "10,12"
},
{
name: "bar",
values: "red,blue,green"
},
{
name: "test",
values: "top,bottom"
}
];
console.log(options(data));

Merge arrays in JS

Suppose I have the following arrays:
var first = [
{ id: 1, name: 'first' },
{ id: 2, name: 'second' },
{ id: 3, name: 'third' }
]
var second = [
{ id: 2, field: 'foo2' },
{ id: 3, field: 'foo3' },
{ id: 4, field: 'foo4' }
]
var third = [
{ id: 2, data: 'some2' },
{ id: 5, data: 'some5' },
{ id: 6, data: 'some6' }
]
I want to merge them to get the following result:
var result = [
{ id: 1, name: 'first', field: undefined, data: undefined },
{ id: 2, name: 'second', field: 'foo2', data: 'some2' },
{ id: 3, name: 'third', field: 'foo3', data: undefined },
{ id: 4, name: undefined, field: 'foo4', data: undefined },
{ id: 5, name: undefined, field: undefined, data: 'some5' },
{ id: 6, name: undefined, field: undefined, data: 'some6' }
]
How could I do it with JavaScript?
You should get all existed keys and after create new Objects with fill "empty" keys:
function mergeArrays(){
var keys = {};
//save all existed keys
for(var i=arguments.length;--i;){
for(var j=arguments[i].length;--j;){
for(var key in arguments[i][j]){
keys[key] = true;
}
}
}
var res = [];
for(var i=arguments.length;--i;){
for(var j=arguments[i].length;--j;){
//set clone of object
var clone = JSON.parse(JSON.stringify(arguments[i][j]));
for(var key in keys){
if(!(key in clone)){
clone[key] = undefined;
}
}
res.push(clone);
}
}
return res;
}
https://jsfiddle.net/x3b0tk3g/
There is no simple solution for what you want. Here is my suggestion.
var first = [
{ id: 1, name: 'first' },
{ id: 2, name: 'second' },
{ id: 3, name: 'third' }
]
var second = [
{ id: 2, filed: 'foo2' },
{ id: 3, field: 'foo3' },
{ id: 4, field: 'foo4' }
];
var third = [
{ id: 2, data: 'some2' },
{ id: 4, data: 'some4' },
{ id: 6, data: 'some6' }
];
var result = {};
first.concat(second,third).forEach(function(item){
var id = item.id;
var row = result[id];
if(!row){
result[id] = item;
return;
}
for(var column in item){
row[column] = item[column];
}
});
var finalResult = Object.keys(result).map(function(id){
return result[id];
});
console.log(finalResult);
fiddle: http://jsfiddle.net/bs20jvnj/2/
function getByProperty(arr, propName, propValue) {
for (var i = 0; i < arr.length; i++) {
if (arr[i][propName] == propValue) return arr[i];
}
}
var limit = first.length + second.length + third.length;
var res = [];
for (var i = 1; i < limit; i++) {
var x = $.extend({}, getByProperty(first, "id", i), getByProperty(second, "id", i), getByProperty(third, "id", i));
console.log(x["id"]);
if (x["id"] === undefined) x["id"] = i;
res.push(x);
}
console.log(res);
There's probably a shorter way to solve this, but this covers all the steps, including ensuring that there are default properties that are undefined if not found. It also takes any number of input arrays, and you can specify what default keys you require if they're not already covered by the keys in the existing objects, so pretty future-proof for your needs.
// merges the key/values of two objects
function merge(a, b) {
var key;
if (a && b) {
for (key in b) {
if (b.hasOwnProperty(key)) {
a[key] = b[key];
}
}
}
return a;
}
function concatenate() {
var result = [];
var args = arguments[0];
for (var i = 0, l = args.length; i < l; i++) {
result = result.concat(args[i]);
}
return result;
}
// return a default object
function getDefault() {
return {
id: undefined,
name: undefined,
data: undefined,
field: undefined
};
}
// loop over the array and check the id. Add the id as a key to
// a temporary pre-filled default object if the key
// doesn't exist, otherwise merge the existing object and the
// new object
function createMergedArray(result) {
var temp = {};
var out = [];
for (var i = 0, l = result.length; i < l; i++) {
var id = result[i].id;
if (!temp[id]) temp[id] = getDefault();
merge(temp[id], result[i]);
}
// loop over the temporary object pushing the values
// into an output array, and return the array
for (var p in temp) {
out.push(temp[p]);
}
return out;
}
function mergeAll() {
// first concatenate the objects into a single array
// and then return the results of merging that array
return createMergedArray(concatenate(arguments));
}
mergeAll(first, second, third);
DEMO

Detect the element in an array [duplicate]

This question already has answers here:
A for-loop that compares two arrays looking for matching values
(4 answers)
Closed 8 years ago.
I have two arrays like
var array1 = [
{
id: '1',
name: 'test'
},
{
id: '2',
name: 'test2'
},
{
id: '3',
name: 'test3'
}
]
var array2=[
{
id: '2',
name: 'test2'
}
]
I want to loop through array 1 and find the same object and add more property in array 1
I have something like
for(var i=0; i < array1.length; i++) {
if(array2[i].id == array1[i].id){
alert('find!')
}
}
I understand my above codes don't work because the index is different. Can someone help me about this issue? Thanks a lot!
It’s time for ECMA5
var array1 = [
{
id: '1',
name: 'test',
foo: {
bar: 'bar',
quux: 'quux'
}
},
{
id: '2',
name: 'test2'
},
{
id: '3',
name: 'test3'
}
];
function equal(objA, objB) {
if(Object.keys(objA).length !== Object.keys(objB).length) {
return false;
}
var areEqual = Object.keys(objA).every(function(key) {
if(typeof objA[key] === "object") {
return equal(objA[key], objB[key]);
}
return objA[key] === objB[key];
});
return areEqual;
}
function hasElement(array, element) {
return array.some(function(el) {
return equal(el, element);
});
}
console.log(hasElement(array1, {
id: '1',
name: 'test',
foo: {
bar: 'bar',
quux: 'quux'
}
}));
Assuming the IDs in array2 are all unique, I would create an object whose keys are the IDs:
var obj2 = {};
for (var i = 0; i < array2.length; i++) {
obj2[array2[i].id] = array2[i];
}
Then I would use this to find matching elements:
for (var i = 0; i < array1.length; i++) {
if (obj2[array1[i].id]) {
alert("find!");
}
}

Categories