update a property in object array - javascript

Is there a way to update a property in object array based on the number of times some other property is present as element in some other array
I have 2 arrays, array1 and array2:
var array1 = ["JOHN", "JACK", "JACK"];
var array2 = [
{count: 9, value: "JACK"},
{count: 9, value: "JOHN"},
{count: 2, value: "TEST"}
];
Expected output :
[
{count: 7, value: "JACK"}, // count = 9 - 2
{count: 8, value: "JOHN"}, // count = 9 - 1
{count: 2, value: "TEST"}
]
In array1, "JACK" is present twice, so I need to reduce count by 2, similarly "JOHN" is present once and hence its reduced by 1, "TEST" is not present so unchanged.
I tried the following
array1.map(item => {
return array2.find( p => p["value"] === item);
});
With this, I am getting the below output,
[
{count: 9, value: "JOHN"},
{count: 9, value: "JACK"},
{count: 9, value: "JACK"}
]
I am not sure whether it can be achieved using single lambda expression.
Thanks in advance!

You can get the result using array .map() and .filter() methods, assuming you are not allowed to change the original array:
var array1 = ["JOHN", "JACK", "JACK"];
var array2 = [{count: 9, value: "JACK"}, {count: 9, value: "JOHN"}, {count: 2, value: "TEST"}]
var result = array2.map(({value, count}) => ({value, count: count - array1.filter(a=>a===value).length}))
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
Using .filter() check how many time a value is present in array1
Then subtract that result from current array2 count.
Then just return a new array of objects with the updated count.

You can use reduce and map
Loop over array1 and create a mapper object with names as key and repetition of names as value
Loop over array2 and see if the name is present in mapper subtract respective value from
const array1 = ["JOHN", "JACK", "JACK"];
const array2 = [{count: 9, value: "JACK"},{count: 9, value: "JOHN"},{count: 2, value: "TEST"}]
const mapper = array1.reduce((op, inp) => {
op[inp] = op[inp] || 0;
op[inp]++;
return op;
}, Object.create(null))
let final = array2.map(({count,value}) =>({
value,
count: count - (mapper[value] || 0)
}))
console.log(final)

Assuming you are allowed to change the original array. ALSO assuming we are not talking 10s of thousand entries since I look up the value in the name array each time:
var array1 = ["JOHN", "JACK", "JACK"];
var array2 = [{count: 9, value: "JACK"},
{count: 9, value: "JOHN"},
{count: 2, value: "TEST"}]
array2.forEach(item => item.count -= array1.filter(val => val === item.value).length);
console.log(array2);
Less resources:
var array1 = ["JOHN", "JACK", "JACK"];
var array2 = [{count: 9, value: "JACK"},
{count: 9, value: "JOHN"},
{count: 2, value: "TEST"}]
// create lookup table
const names = array1.reduce((arr,cur) => { arr[cur] = (arr[cur]||0) + 1; return arr;},{})
// subtract if present
array2.forEach(item => item.count -= (names[item.value] || 0));
console.log(array2);

Related

Compare two Arrays and Insert Null Vallues

I have two arrays:
var array1 = [{id: 1, time: 100}, {id: 2, time: 200}, {id: 3, time: 300}];
var array2 = [{id: 1, time: 100}, {id: 3, time: 300}];
And I would like for array2 to be changed to
var array2 = [{id: 1, time: 100}, null, {id: 3, time: 300}];
The question is how can I compare the two arrays and look at their time and then insert null in the missing locations for each array.
Any help is appreciated!
const arr1 = [{id: 1, time: 100}, {id: 2, time: 200}, {id: 3, time: 300}];
const arr2 = [{id: 1, time: 100}, {id: 3, time: 300}, {id: 3, time: 400}];
const uniqueTimes = [...arr1, ...arr2]
.filter((e, i, a) => a.findIndex(x => x.time === e.time) === i)
const res1 = uniqueTimes.map(e =>
arr1.find(x => x.time === e.time) ?? null
)
const res2 = uniqueTimes.map(e =>
arr2.find(x => x.time === e.time) ?? null
)
console.log(res1)
console.log(res2)
Your example is a little misleading. Your description of the prompt says entries can be missing in both arrays, right? My example has 200 missing in array2, and 400 missing in array1
var array1 = [{ id: 1, time: 100 }, { id: 2, time: 200 }, { id: 3, time: 300 }];
var array2 = [{ id: 1, time: 100 }, { id: 3, time: 300 }, { id: 1, time: 400 }];
// get all possible times, sort them
const allSortedTimes = array1.map(({ time }) => time).concat(array2.map(({ time }) => time)).sort((a, b) => a - b)
// only use uniq times
const allUniqTimes = [...new Set(allSortedTimes)]
// now that we have all the possible times,
// we go over each array and check to see if that time exists
const insertedArray1 = allUniqTimes.map((uniqTime) => {
return array1.find(({ time }) => time === uniqTime) ?? null
})
const insertedArray2 = allUniqTimes.map((uniqTime) => {
return array2.find(({time}) => time === uniqTime) ?? null
})
console.log(insertedArray1)
console.log(insertedArray2)
Here's one way to do it.
var array1 = [{
id: 1,
time: 100
}, {
id: 2,
time: 200
}, {
id: 3,
time: 300
}];
var array2 = [{
id: 1,
time: 100
}, {
id: 3,
time: 300
}];
const fixArray = (a, maxTime) => {
let inc = 100,
start = inc,
tmp = [];
// first make sure we have it in order
//a = a.sort((a, b) => (a.time < b.time) ? -1 : 1)
while (start < maxTime) {
let t = a.filter(el => el.time === start)
if (t.length === 0) tmp.push(null);
else tmp.push(t[0])
start += inc;
}
return tmp
}
array2 = fixArray(array2, 1000);
console.log(array2)
const getKey=(id,time)=>id+"_"+time //Provides unique key based on object values
var array1 = [{id: 1, time: 100}, {id: 2, time: 200}, {id: 3, time: 300}];
var array2 = [{id: 0, time: 5},{id: 1, time: 100},{id: 11, time: 250}, {id: 3, time: 300},{id: 5, time: 500}];
let keysOfArray1=[]//array1 unique keys
let keysOfArray2=[]//array2 unique keys
array1.map(item=>{
keysOfArray1.push(getKey(item.id,item.time)); // collects array1 unique keys
return item
}).concat(array2.map(item=>{//concat array2 values with array1
keysOfArray2.push(getKey(item.id,item.time)) // collects array2 unique keys
return item
})).sort((a,b)=>a.time-b.time || a.id-b.id).reduce((prev,current)=>{ //Sort by time & then Id
let keyName=getKey(current.id,current.time)
if(!prev.includes(keyName)){// To consider all objects only once
array1[prev.length]=keysOfArray1.includes(keyName)?current:null
array2[prev.length]=keysOfArray2.includes(keyName)?current:null
prev.push(keyName)
}
return prev
},[])
console.log(array1);
console.log(array2);

Flatten whilst splitting into smaller objects

I am having these kind of array structure.
[{ "primary_product": "Blueberry",
"list_of_products": ["Raspberry","Strawberry","Blackberry"]}]
I want to destructure the pattern and make it like this below
[{"id": 1,"value":"Blueberry"},{"id": 2,"value":"Raspberry"},{"id": 3,"value":"Strawberry"}, …]
Primary product will be the first product and then make the array of strings into key/value pair. How to do this using es6?
All you need is basic functions like forEach and push. I would recommend learning these.
let arr1 = [{ "primary_product": "Blueberry", "list_of_products": ["Raspberry","Strawberry","Blackberry"]}]
arr2 = [{ id: 1, value: arr1[0].primary_product }]
arr1[0].list_of_products.forEach((element) => {
arr2.push({ id: arr2.length + 1, value: element })
})
Here's a one-liner using map on the list_of_products:
const arr = ['Raspberry','Strawberry','Blackberry'];
return arr.map((val, i) => {return {id: i+1, value: val}});
This is the result:
[
{ id: 1, value: 'Raspberry' },
{ id: 2, value: 'Strawberry' },
{ id: 3, value: 'Blackberry' }
]
Note that the callback to map includes (currentValue, index, arr).
To make things slightly easier for the eyes I've simplified the structure:
const p = [ { a: 100, b: [101, 102, 103]}
, { a: 200, b: [201, 202, 203]}];
You can flatten all the numbers into a single list i.e. [100, 101, 102, 103, 200, 201, 202, 203] with:
p.flatMap(({a, b}) => [a, ...b]);
To get closer to what you're after, let's first create an id function that will return the next number:
const id = (n => () => ++n)(0);
id(); //=> 1
id(); //=> 2
id(); //=> 3
// …
Then let's create a function obj that takes an x and wraps it into an object:
const obj => x => ({id: id(), value: x});
obj(100); //=> {id: 1, value: 100);
obj(200); //=> {id: 2, value: 200);
// …
Then you can do:
p.flatMap(({a, b}) => [obj(a), ...b.map(obj)]);
//=> [ {id: 1, value: 100}
//=> , {id: 2, value: 101}
//=> , {id: 3, value: 102}
//=> , {id: 4, value: 103}
//=> , {id: 5, value: 200}
//=> , {id: 6, value: 201}
//=> , {id: 7, value: 202}
//=> , {id: 8, value: 203}]

how get common elements of 2 different arrays and return an object containing the common elements

I have 2 arrays of objects
var array1 = [
{id: 1, name:'fruit', rating:5},
{id: 4, name:'vegetable', rating: 3},
{id: 8, name:'meat', rating:1}
];
var array2 = [
{alimentId: 1, quantity: 2},
{alimentId: 4, quantity: 2},
{alimentId: 8, quantity: 4}
]
and I want to get a new the array1 such that
var array = [
{id: 1, name:'fruit'},
{id: 4, name:'vegetable'},
]
which has only the elements with quantity 2 matching the alimentId with the id.
I'm always getting confused with arrays and objects manipulations.. Please help
I believe the following code will solve your problem:
const func = (arr1, arr2) => {
return arr1.filter(obj => {
const objToCheck = arr2.filter(element => element.alimentId === obj.id);
return objToCheck[0].quantity === 2;
});
};
You also can send the wanted value(2) and the key name(quantity) as params.
var array1 = [
{id: 1, name:'fruit', rating:5},
{id: 4, name:'vegetable', rating: 3},
{id: 8, name:'meat', rating:1}
];
var array2 = [
{alimentId: 1, quantity: 2},
{alimentId: 4, quantity: 2},
{alimentId: 8, quantity: 4}
]
function filter(array1, array2) {
return array1
.filter(it => array2 // filter array1 by array2
.filter(it => it.quantity === 2) // filter your array2 by field quantity = 2
.map(it => it.alimentId) // pull out array of alimentId
.includes(it.id) // check array2.alimentId includes array1.id
)
}
console.log(filter(array1, array2))
use this function
const common_elements = (arr1, arr2, quantity) => {
let res = []
arr1.forEach(el1 => {
arr2.forEach(el2 => {
if(el1.id === el2.alimentId && el2.quantity === quantity) {
res.push(el1)
}
});
});
return res
}
You can do a reduce:
var array3 = array1.reduce((acc ,val ,index) => {
if (val.id=array2[index].alimentId) {
acc =[...acc, {id: val.id, name: val.name}]
}
return acc;
},[]);
var array1 = [
{id: 1, name:'fruit', rating:5},
{id: 4, name:'vegetable', rating: 3},
{id: 8, name:'meat', rating:1}
];
var array2 = [
{alimentId: 1, quantity: 2},
{alimentId: 4, quantity: 2},
{alimentId: 8, quantity: 4}
]
const commonArray = array2.filter(item => item.quantity === 2 && array1.find(el => el.id===item.alimentId));
console.log(commonArray)

Filtering an array of values by array of object's value

In Javascript we have an array:
let arr1 = [1, 2, 3, 4, 5];
...and an array of objects:
let arr2 = [ {name: "banana", id: 1},
{name: "mango", id: 3} ];
I want to remove all the elements from arr1 where arr2's id = arr1's value and return an array like this:
[2, 4, 5]
Here is what I've tried, but it doesn't seem to work.
let newArr = arr1.filter(
x => !arr2.includes(e => e.id === x)
)
How can I achieve this? I can use lodash as well as ES6.
The .includes() method doesn't allow you to pass a method into it to define equality. You can use .some() instead, which does allow you to specify this:
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [
{name: "banana", id: 1},
{name: "mango", id: 3}
];
const newArr = arr1.filter(x => !arr2.some(e => e.id === x))
console.log(newArr);
A more efficient approach would be to grab all the id properties from the objects in your array and put then in a Set for quick look-up. Then use .filter() with .has() like so:
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [
{name: "banana", id: 1},
{name: "mango", id: 3}
];
const idSet = new Set(arr2.map(({id}) => id));
const newArr = arr1.filter(x => !idSet.has(x))
console.log(newArr);
You can first create a temporary of id and age, then use filter() with includes()
let arr1 = [1, 2, 3, 4, 5];
let arr2 = [ {name: "banana", id: 1},
{name: "mango", age: 3} ];
let temp = arr2.map(i => i.id || i.age);
let res = arr1.filter(i => !temp.includes(i));
console.log(res);
let arr1 = [1, 2, 3, 4, 5];
let arr2 = [ {name: "banana", id: 1},
{name: "mango", id: 3} ];
arr1.map((item,index) =>
arr2.map(object => {
if(item == object.id) arr1.splice(index,1)
})
)
console.warn(arr1) /// output [2, 4, 5]

Filtering using two array of objects using JavaScript

I have an array as shown:
var arrOne = [{id: 3},{id: 8},{id: 12}];
And another array as shown:
var arrTwo = [
{id: 1, val: 'Adam'},
{id: 3, val: 'Bailey'},
{id: 8, val: 'Cathy'},
{id: 12, val: 'David'},
{id: 15, val: 'Earl'}
];
I want to iterate arrTwo based on arrOne, and get the val values out of arrTwo.
So the result should be:
var result = ['Bailey', 'cathy', 'David'];
Tried concatenating .map with .filter:
arrOne.map(arOne => arrTwo.filter(artwo => {
if(arOne.id === artwo.id) {
return artwo.val
} else {
return false;
}
}));
But it gives me all, and where it is false it adds false there, which I don't want.
Any ideas where I am going wrong will be appreciated.
Editing as per norbitrial's answer:
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [
{id: 1, val: 'Adam'},
{id: 3, val: 'Bailey'},
{id: 8, val: 'Cathy'},
{id: 12, val: 'David'},
{id: 15, val: 'Earl'}
];
const result = arrOne.map(({id}) => arrTwo.find(e => {
const someCond = someConditionaEval();
if(someCond && e.id === id) {
return e;
} else {
return false;
}
}).val); //this breaks
Using .map() and .find() combination:
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [{id: 1, val: 'Adam'}, {id: 3, val: 'Bailey'}, {id: 8, val: 'Cathy'}, {id: 12, val: 'David'}, {id: 15, val: 'Earl'}];
const result = arrOne.map(({id}) => arrTwo.find(e => e.id === id).val);
console.log(result);
I hope this helps!
You can use .filter() method on arrTwo and then using .includes() method get the filtered objects from arrTwo and then finally using .map() get only the val property values from each filtered object like:
var arrOne = [{id: 3},{id: 8},{id: 12}];
var arrTwo = [{id:1,val:"Adam"},{id:3,val:"Bailey"},{id:8,val:"Cathy"},{id:12,val:"David"},{id:15,val:"Earl"}];
var result = arrTwo.filter(a => arrOne.map(o=>o.id).includes(a.id)).map(o=>o.val)
console.log( result )
You could take an object with the values and then map the wanted values.
var arrOne = [{ id: 3 }, { id: 8 }, { id: 12 }],
arrTwo = [{ id: 1, val: 'Adam' }, { id: 3, val: 'Bailey' }, { id: 8, val: 'Cathy' }, { id: 12, val: 'David' }, { id: 15, val: 'Earl' }],
values = arrTwo.reduce((r, { id, val }) => (r[id] = val, r), {}),
result = arrOne.map(({ id }) => values[id]);
console.log(result);
Create a Map of val by id from arrTwo, and then map arrOne, and extract the val from the Map using the id.
Why I prefer creating a Map/dictionary (object) instead of using Array.map() with Array.find()?
Because of the complexity - Array.map() with Array.find(), for example, is O(n * m), while creating a Map and then using Array.map() to get the values is O(n + m). However, if you've got two small arrays, this shouldn't actually hurt actual performance.
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [{id: 1, val: 'Adam'}, {id: 3, val: 'Bailey'}, {id: 8, val: 'Cathy'}, {id: 12, val: 'David'}, {id: 15, val: 'Earl'}];
const valById = new Map(arrTwo.map(({ id, val }) => [id, val]));
const result = arrOne.map(o => valById.get(o.id));
console.log(result);
Build an object from arrTwo to gather val's in one iteration.
use map on arrOne and get val from above object.
const update = (arr1, arr2) => {
const all = Object.fromEntries(arr2.map(({ id, val }) => [id, val]));
return arr1.map(({ id }) => all[id]);
};
var arrOne = [{ id: 3 }, { id: 8 }, { id: 12 }];
var arrTwo = [
{ id: 1, val: "Adam" },
{ id: 3, val: "Bailey" },
{ id: 8, val: "Cathy" },
{ id: 12, val: "David" },
{ id: 15, val: "Earl" }
];
console.log(update(arrOne, arrTwo));

Categories