I want to update the first array's value on the basis of the second array.
For Example. This is my first Array
let a = [{id:"1",name:"none"},{id:"2",name:"two"}]
This is my second Array
let b = [{id:"1",name:"one"}]
This is what I want as an output.
console.log(a) //[{id:"1",name:"one"},{id:"2",name:"two"}]
Currently, I'm trying to achieve this by using array.filters and array.foreach but I'm getting an empty array.
a = a.filter((item) => {
b.forEach(i => {
if(item.id == i.id){
return i
}else{
return item
}
})
})
Ps. I don't want to create a new Array, I just want to update a's value on the basis of b. Thanks!
This is Test Case 2.
Array 1
let x = [
{id:"1", status:"occupied"},
{id:"2", status:"occupied"},
{id:"3", status:"occupied"},
{id:"4", status:"occupied"},
{id:"5", status:"occupied"},
{id:"6", status:"occupied"},
{id:"7", status:"occupied"}
]
Array 2
let z = [
{id:"1",status:"cleaning"},
{id:"3",status:"cleaning"},
{id:"5",status:"cleaning"},
{id:"7",status:"cleaning"},
{id:"2",status:"cleaning"},
]
Object.assign
let y = Object.assign(x,z);
console.log(y);//
I'm getting this output which is messed up.
{id: "1", status: "cleaning"}
{id: "3", status: "cleaning"}
{id: "5", status: "cleaning"}
{id: "7", status: "cleaning"}
{id: "2", status: "cleaning"}
{id: "6", status: "occupied"}
{id: "7", status: "occupied"}
You can use Object.assign which merges two objects by taking care of duplicates`
let c = Object.assign(a,b);
console.log(c);
How about something like this:
const a = [{id:"1",name:"none"},{id:"2",name:"two"}];
const b = [{id:"1",name:"one"}];
for (const item of a) {
const bFound = b.find(bItem => bItem.id === item.id);
if (bFound) {
item.name = bFound.name;
}
}
console.log(a);
try this:
let a = [{id:"1",name:"none"},{id:"2",name:"two"}]
let b = [{id:"1",name:"one"}]
var merge = (a, b, p) => a.filter( aa => ! b.find ( bb => aa[p] === bb[p]) ).concat(b)
console.log(merge(a, b, "id"))
Related
i'm trying to duplicate objects based on two properties that have multiple values differentiated by a comma.
For example:
I have an object
const obj = {
id: 1
date: "2021"
tst1: "111, 222"
tst2: "AAA, BBB"
}
And I would like the result to be an array of 2 objects in this case (because there are 2 values in tst1 OR tst2, these 2 properties will always have the same nr of values differentiated by a comma)
[{
id: 1,
date: "2021",
tst1: "111",
tst2: "AAA",
},
{
id: 1,
date: "2021",
tst1: "222",
tst2: "BBB",
}]
What I tried is this:
I created a temporary object
const tempObject = {
id: obj.id,
date: obj.date,
}
And then I would split and map the property that has multiple values, like this:
cont newObj = obj.tst1.split(",").map(function(value) {
let finalObj = {}
return finalObj = {
id: tempObject.id,
date: tempObject.date,
tst1: value,
})
And now, the newObj is an array of objects and each object contains a value of tst1.
The problem is I still have to do the same for the tst2...
And I was wondering if there is a simpler method to do this...
Thank you!
Here is an example that accepts an array of duplicate keys to differentiate. It first maps them to arrays of entries by splitting on ',' and then trimming the entries, then zips them by index to create sub-arrays of each specified property, finally it returns a result of the original object spread against an Object.fromEntries of the zipped properties.
const mapDuplicateProps = (obj, props) => {
const splitProps = props.map((p) =>
obj[p].split(',').map((s) => [p, s.trim()])
);
// [ [[ 'tst1', '111' ], [ 'tst1', '222' ]], [[ 'tst2', 'AAA' ], [ 'tst2', 'BBB' ]] ]
const dupeEntries = splitProps[0].map((_, i) => splitProps.map((p) => p[i]));
// [ [[ 'tst1', '111' ], [ 'tst2', 'AAA' ]], [[ 'tst1', '222' ], [ 'tst2', 'BBB' ]] ]
return dupeEntries.map((d) => ({ ...obj, ...Object.fromEntries(d) }));
};
const obj = {
id: 1,
date: '2021',
tst1: '111, 222',
tst2: 'AAA, BBB',
};
console.log(mapDuplicateProps(obj, ['tst1', 'tst2']));
Not sure if that's what you're searching for, but I tried making a more general use of what you try to do:
const duplicateProperties = obj => {
const properties = Object.entries(obj);
let acc = [{}];
properties.forEach(([key, value]) => {
if (typeof value === 'string' && value.includes(',')) {
const values = value.split(',');
values.forEach((v, i) => {
if (!acc[i]) {
acc[i] = {};
}
acc[i][key] = v.trim();
});
} else {
acc.forEach(o => o[key] = value);
}
});
return acc;
};
const obj = {
id: 1,
date: '2021',
tst1: '111, 222',
tst2: 'AAA, BBB',
};
console.log(duplicateProperties(obj));
You could start by determining the length of the result using Math.max(), String.split() etc.
Then you'd create an Array using Array.from(), returning the correct object for each value of the output index.
const obj = {
id: 1,
date: "2021",
tst1: "111, 222",
tst2: "AAA, BBB",
}
// Determine the length of our output array...
const length = Math.max(...Object.values(obj).map(s => (s + '').split(',').length))
// Map the object using the relevant index...
const result = Array.from({ length }, (_, idx) => {
return Object.fromEntries(Object.entries(obj).map(([key, value]) => {
const a = (value + '').split(/,\s*/);
return [key, a.length > 1 ? a[idx] : value ]
}))
})
console.log(result)
.as-console-wrapper { max-height: 100% !important; }
I have the below array of objects and I want to check if two different users are present in this array .if present i have to run some logic
let result = [{"name": "ABC"},{"name": "CDE"},{"name": "FGH"},{"name": "XYZ"}];
var newArr = [];
var hasMatch = result.filter(function(val) {
if (val.name == "FGH"){
newArr.push(val)
} else if (val.name == "ABC") {
newArr.push(val)
}
});
console.log(newArr)
if (newArr.length == 2) {
//do logic
}
It's working as expected but I'm looking for a different approach for this. could someone advise?
Not optimized for speed, but does the job
let arr = [
{
"name": "ABC"
},
{
"name": "CDE"
},
{
"name": "FGH"
},
{
"name": "XYZ"
}
];
let users = ["ABC", "XYZ"]
let hasAllUsers = users.every(user => arr.some(item => item.name == user))
console.log(hasAllUsers)
// if(hasAllUser) {...}
It's a pretty roundabout way to zero in on the logic you're trying to express. Note how the result in hasMatch is never even used. That's really all you're looking for, does the array "have the values".
There's no need to push values to another array and check if that array has values. Just check of the original array has them.
Which could be as simple as:
let result = [{"name": "ABC"},{"name": "CDE"},{"name": "FGH"},{"name": "XYZ"}];
if (result.filter(r => r.name === "FGH" || r.name === "ABC").length === 2) {
// do logic
}
Or if you want to refactor the condition into a variable:
let result = [{"name": "ABC"},{"name": "CDE"},{"name": "FGH"},{"name": "XYZ"}];
let hasMatch = result.filter(r => r.name === "FGH" || r.name === "ABC").length === 2;
if (hasMatch) {
// do logic
}
Or a bit more verbose for clarity:
let result = [{"name": "ABC"},{"name": "CDE"},{"name": "FGH"},{"name": "XYZ"}];
let filteredResult = result.filter(r => r.name === "FGH" || r.name === "ABC");
let hasMatch = filteredResult.length === 2;
if (hasMatch) {
// do logic
}
You can simply create another array with the valid users and filter your array to match each items that are this array.
This can be done using the Array#includes method
const users = [{"name": "ABC"},{"name": "CDE"},{"name": "FGH"},{"name": "XYZ"}];
const validUsers = ["ABC", "FGH", "AnotherUser"];
const matchUsers = users.filter(user => validUsers.includes(user.name))
console.log(matchUsers)
You could count the wanted names.
const
data = [{ name: "ABC" }, { name: "CDE" }, { name: "FGH" }, { name: "XYZ" }],
names = ['ABC', 'FGH'],
result = data.reduce((t, { name }) => t + names.includes(name), 0);
console.log(result);
Try using a named function and pass in the array, key, and one or more values with the rest operator ...values. Use .flatMap() to filter with
[...values].includes(obj[key])
// ["ABC", "XYZ"].includes(obj.name)
and any non-match returns an empty array []. The final return is an array with a sub-array and the length of said sub-array.
const result = [["ABC", "XYZ"], 2]
// result[0][0] = "ABC"
// result[0][1] = "XYZ"
// result[1] = 2
const arr = [{"name": "ABC"},{"name": "CDE"},{"name": "FGH"},{"name": "XYZ"}];
function hasMatch(array, key, ...values) {
const result = array.flatMap((obj, idx) =>
[...values].includes(obj[key]) ? obj : []);
return [result, result.length];
}
console.log(hasMatch(arr, "name", "ABC", "XYZ"));
console.log(hasMatch(arr, "name", "FGH", "IJK", "LMN", "ABC", "XYZ"));
I have 2 arrays :
[{id:1,name:"name"},{id:2,name:"name2"} ,{id:3,name:"name3"}]
[{id:1,date:"123"},{id:2,date:"456"}]
Array 1 should be updated only if the id is equal :
So the array 1 will looks like
It should not create a new array . Only update the array 1 based on array 2
[{id:1,name:"name",date:"123"},{id:2,name:"name2",date:"456"} ,{id:3,name:"name3"}]
I managed to do that with for loop on array2 and inside the for filter like the following :
._filter(array1,function(item){
If(item.id=array2.id)
Do smth and update the array1.date
})
How do I doing that in he best way ? Using underscore.js
You can do something like this:
Iterate over array1 and check if the id of each item exists in array2 by using the some() method.
var arr1 = [{id:1,name:"name"},{id:2,name:"name2"} ,{id:3,name:"name3"}];
var arr2 = [{id:1,date:"123"},{id:2,date:"456"}];
var missing = [];
arr1.forEach( (item1, i) => {
var isExist = arr2.some(item2 => item2.id === item1.id)
if(!isExist) {
missing.push(i);
}
})
missing.forEach(item => {
arr2.push(arr1[item]);
})
console.log(arr2);
reference for some()
Try this :
var a = [{id:1,name:"name"},{id:2,name:"name2"} ,{id:3,name:"name3"}] ;
var b = [{id:1,date:"123"},{id:2,date:"456"}] ;
var i = 0, j = 0 ;
while( i < a.length ) {
j = 0 ;
while( j < b.length) {
if ( a[i].id === b[j].id )
Object.assign( a[i] , b[j] );
j++;
}
i++;
}
console.log(a) ;
You can use forEach to iterate over the second array and use findIndex to get the matched element from first array. If the id matches then update the object in the first array
let arr1 = [{
id: 1,
name: "name"
}, {
id: 2,
name: "name2"
}, {
id: 3,
name: "name3"
}]
let arr2 = [{
id: 1,
date: "123"
}, {
id: 2,
date: "456"
}]
arr2.forEach(function(acc) {
let findArry1Index = arr1.findIndex(function(item) {
return item.id === acc.id;
});
if (findArry1Index !== -1) {
arr1[findArry1Index].date = acc.date;
}
});
console.log(arr1)
You can do it using native language like this:
const arr1 = [{id:1,name:"name"},{id:2,name:"name2"} ,{id:3,name:"name3"}];
const arr2 = [{id:1,date:"123"},{id:2,date:"456"}];
arr1.forEach((ele) => {
const match = arr2.find(item => ele.id === item.id) || {};
Object.assign(ele, match);
});
console.log(arr1);
var a = [{id:1,name:"name"},{id:2,name:"name2"} ,{id:3,name:"name3"}];
var b = [{id:1,date:"123"},{id:2,date:"456"}];
a = _.map(a, function(e) { return _.extend(e, _.findWhere(b, {id: e.id})); });
a results in:
0: {id: 1, name: "name", date: "123"}
1: {id: 2, name: "name2", date: "456"}
2: {id: 3, name: "name3"}
However, I guess this qualifies as "creating a new array"? Maybe it can serve as an inspiration though ¯\_(ツ)_/¯
You can use underscore's indexBy function to index your second array by id, and then simply use Object.assign(...) to update your first array's elements with their corresponding match by performing a lookup in the indexed elements object.
let arr1 = [{id:1, name:"name"}, {id:2, name:"name2"}, {id:3, name:"name3"}]
let arr2 = [{id:1, date:"123"}, {id:2, date:"456"}]
const arr2Groups = _.indexBy(arr2, e => e.id);
arr1.forEach(e => Object.assign(e, arr2Groups[e.id] || {}));
console.log(arr1);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js"></script>
I have a problem! I am creating an rating app, and I have come across a problem that I don't know how to solve. The app is react native based so I am using JavaScript.
The problem is that I have multiple objects that are almost the same, I want to take out the average value from the values of the "same" objects and create a new one with the average value as the new value of the newly created object
This array in my code comes as a parameter to a function
var arr = [
{"name":"foo","value":2},
{"name":"foo","value":5},
{"name":"foo","value":2},
{"name":"bar","value":2},
{"name":"bar","value":1}
]
and the result I want is
var newArr = [
{"name":"foo","value":3},
{"name":"bar","value":1.5},
]
If anyone can help me I would appreciate that so much!
this is not my exact code of course so that others can take help from this as well, if you want my code to help me I can send it if that's needed
If you have any questions I'm more than happy to answer those
Iterate the array with Array.reduce(), and collect to object using the name values as the key. Sum the Value attribute of each name to total, and increment count.
Convert the object back to array using Object.values(). Iterate the new array with Array.map(), and get the average value by dividing the total by count:
const arr = [{"name":"foo","Value":2},{"name":"foo","Value":5},{"name":"foo","Value":2},{"name":"bar","Value":2},{"name":"bar","Value":1}];
const result = Object.values(arr.reduce((r, { name, Value }) => {
if(!r[name]) r[name] = { name, total: 0, count: 0 };
r[name].total += Value;
r[name].count += 1;
return r;
}, Object.create(null)))
.map(({ name, total, count }) => ({
name,
value: total / count
}));
console.log(result);
I guess you need something like this :
let arr = [
{name: "foo", Value: 2},
{name: "foo", Value: 5},
{name: "foo", Value: 2},
{name: "bar", Value: 2},
{name: "bar", Value: 1}
];
let tempArr = [];
arr.map((e, i) => {
tempArr[e.name] = tempArr[e.name] || [];
tempArr[e.name].push(e.Value);
});
var newArr = [];
$.each(Object.keys(tempArr), (i, e) => {
let sum = tempArr[e].reduce((pv, cv) => pv+cv, 0);
newArr.push({name: e, value: sum/tempArr[e].length});
});
console.log(newArr);
Good luck !
If you have the option of using underscore.js, the problem becomes simple:
group the objects in arr by name
for each group calculate the average of items by reducing to the sum of their values and dividing by group length
map each group to a single object containing the name and the average
var arr = [
obj = {
name: "foo",
Value: 2
},
obj = {
name: "foo",
Value: 5
},
obj = {
name: "foo",
Value: 2
},
obj = {
name: "bar",
Value: 2
},
obj = {
name: "bar",
Value: 1
}
]
// chain the sequence of operations
var result = _.chain(arr)
// group the array by name
.groupBy('name')
// process each group
.map(function(group, name) {
// calculate the average of items in the group
var avg = (group.length > 0) ? _.reduce(group, function(sum, item) { return sum + item.Value }, 0) / group.length : 0;
return {
name: name,
value: avg
}
})
.value();
console.log(result);
<script src="http://underscorejs.org/underscore-min.js"></script>
In arr you have the property Value and in newArr you have the property value, so I‘ll assume it to be value both. Please change if wished otherwise.
var map = {};
for(i = 0; i < arr.length; i++)
{
if(typeof map[arr[i].name] == ‘undefined‘)
{
map[arr[i].name] = {
name: arr[i].name,
value: arr[i].value,
count: 1,
};
} else {
map[arr[i].name].value += arr[i].value;
map[arr[i].name].count++;
}
var newArr = [];
for(prop in map)
{
map[prop].value /= map[prop].count;
newArr.push({
name: prop,
value: map[prop].value
});
}
delete map;
I am trying to filter an array, based on some nested object. I prepared some Fiddle
Input array looks like this:
let arrayOfElements =
[
{
"name": "a",
"subElements":
[
{"surname": 1},
{"surname": 2}
]
},
{
"name": "b",
"subElements":
[
{"surname": 3},
{"surname": 1}
]
},
{
"name": "c",
"subElements":
[
{"surname": 2},
{"surname": 5}
]
}
];
I want the output for this case, to look like this:
let filteredArray =
[
{
"name": "a",
"subElements":
[
{"surname": 1}
]
},
{
"name": "b",
"subElements":
[
{"surname": 1}
]
}
];
I am using this formula to do that:
let filteredArray = arrayOfElements.filter((element) => element.subElements.some((subElement) => subElement.surname === 1));
Output is almost good, but it returns objects with all objects with surnames (better check that fiddle :D), instead of cutting them away. How can i improve the filtering ?
This way you can go as deep as you want in an array and filter elements at any level,
arrayOfElements.map((element) => {
return {...element, subElements: element.subElements.filter((subElement) => subElement.surname === 1)}
})
Spread operator will expand element and then filtered subElements will override the subElements in element.
After you call filter, you need to pipe the results to map, like this:
let filteredArray = arrayOfElements
.filter((element) =>
element.subElements.some((subElement) => subElement.surname === 1))
.map(element => {
let newElt = Object.assign({}, element); // copies element
return newElt.subElements.filter(subElement => subElement.surname === '1');
});
I am assuming here that you don't want to manipulate the original array. So, I am using Object.assign.
let filteredArray = arrayOfElements
.filter((element) =>
element.subElements.some((subElement) => subElement.surname == 1))
.map(element => {
return Object.assign({}, element, {subElements : element.subElements.filter(subElement => subElement.surname == 1)});
});
Just improved the answers above
let elements =
[
{
"name": "a",
"subElements":
[
{"surname": 1},
{"surname": 2}
]
},
{
"name": "b",
"subElements":
[
{"surname": 3},
{"surname": 1}
]
},
{
"name": "c",
"subElements":
[
{"surname": 2},
{"surname": 5}
]
}
];
var value = 1;
var filteredArray = elements
.filter(element => element.subElements
.some(subElement => subElement.surname === value)
)
.map(element => {
let n = Object.assign({}, element, {'subElements': element.subElements.filter(
subElement => subElement.surname === value
)})
return n;
})
console.log(filteredArray)
Try this solution:
data_filter = arrayOfElements.filter(function (element) {
return element.subElements.some( function (subElement) {
return subElement.surname === surname
});
});
You can make it generic as well:
Logic
Find all distinct surnames and loop over them
Filter every object to check if surnames exists. If yes, copy object using Object.assign and set subElements value to filtered list.
Create a temp array to hold all similar objects and push copied object to it.
Push this array to final array on every iteration of distinct surname.
Sample
let arrayOfElements=[{name:"a",subElements:[{surname:1},{surname:2}]},{name:"b",subElements:[{surname:3},{surname:1}]},{name:"c",subElements:[{surname:2},{surname:5}]}];
let distinct_surnames = [];
arrayOfElements.forEach(function(el) {
el.subElements.forEach(function(s) {
if (distinct_surnames.indexOf(s.surname) < 0) distinct_surnames.push(s.surname)
});
})
let result = [];
distinct_surnames.forEach(function(sn) {
let inter = [];
arrayOfElements.forEach(function(el) {
let f = el.subElements.filter(function(sub) {
return sub.surname === sn;
});
if (f.length > 0) {
let _tmp = Object.assign({}, el);
_tmp.subElements = f;
inter.push(_tmp);
}
});
result.push(inter);
})
console.log(result)
Note: Arrow functions are used to keep the reference of this. If you are not using this inside function, you can use normal functions as well.
function display_message() {
let arrayOfElements = [{
"name": "a",
"subElements": [{
"surname": 1
}, {
"surname": 2
}]
}, {
"name": "b",
"subElements": [{
"surname": 3
}, {
"surname": 1
}]
}, {
"name": "c",
"subElements": [{
"surname": 2
}, {
"surname": 5
}]
}];
// console.log(arrayOfElements);
var surname = 1;
let filteredArray = arrayOfElements.filter((element) => element.subElements.some((subElement) => subElement.surname === surname));
for(var data in filteredArray){
filteredArray[data].subElements = {"surname": surname};
}
console.log(filteredArray);
}
<input type="button" onclick="display_message();" value="click"/>
let filteredArray = arrayOfElements
.filter((element) =>
element.subElements.some((subElement) => subElement.surname === 1))
.map(element => {
let newElt = Object.assign({}, element); // copies element
newElt.subElements = newElt.subElements.filter(subElement => subElement.surName === '1');
return newElt;
});
is more correctly