Find all index based on condition - javascript

How can I get all the indexes based on a condition for an array of objects?
I have tried the code below, but it's returning only the first occurrence.
a = [
{prop1:"abc",prop2:"yutu"},
{prop1:"bnmb",prop2:"yutu"},
{prop1:"zxvz",prop2:"qwrq"}];
index = a.findIndex(x => x.prop2 ==="yutu");
console.log(index);

findIndex will return only one matching index, You can check value against property prop2 using filter
a = [
{prop1:"abc",prop2:"yutu"},
{prop1:"bnmb",prop2:"yutu"},
{prop1:"zxvz",prop2:"qwrq"}];
const allIndexes = a
.map((e, i) => e.prop2 === 'yutu' ? i : -1)
.filter(index => index !== -1);
console.log(allIndexes);
// This is one liner solution might not work in older IE ('flatMap')
const notSupportedInIE =a.flatMap((e, i) => e.prop2 === 'yutu' ? i : []);
console.log(notSupportedInIE);

Try Array.reduce
a = [
{prop1:"abc",prop2:"yutu"},
{prop1:"bnmb",prop2:"yutu"},
{prop1:"zxvz",prop2:"qwrq"}];
index = a.reduce((acc, {prop2}, index) => prop2 ==="yutu" ? [...acc, index] : acc, []);
console.log(index);

You can use normal for loop and when ever the prop2 matches push the index in the array
const a = [{
prop1: "abc",
prop2: "yutu"
},
{
prop1: "bnmb",
prop2: "yutu"
},
{
prop1: "zxvz",
prop2: "qwrq"
}
];
const indArr = [];
for (let i = 0; i < a.length; i++) {
if (a[i].prop2 === 'yutu') {
indArr.push(i)
}
}
console.log(indArr);

The findIndex method returns the index of the first element in the
array that satisfies the provided testing function. Otherwise, it
returns -1, indicating that no element passed the test. - MDN
You can use reduce here:
const a = [
{ prop1: "abc", prop2: "yutu" },
{ prop1: "bnmb", prop2: "yutu" },
{ prop1: "zxvz", prop2: "qwrq" },
];
const result = a.reduce((acc, curr, i) => {
if (curr.prop2 === "yutu") acc.push(i);
return acc;
}, []);
console.log(result);

You can simply iterate through objects, e.g.
function getIndexes(hystack, nameOfProperty, needle) {
const res = new Array();
for (const [i, item] of hystack.entries()) {
if (item[nameOfProperty] === needle) res.push(i);
}
return res;
}
const items =
[
{prop1:"a", prop2:"aa"},
{prop1:"b", prop2:"bb"},
{prop1:"c", prop2:"aa"},
{prop1:"c", prop2:"bb"},
{prop1:"d", prop2:"cc"}
];
const indexes = getIndexes(items, 'prop2', 'bb');
console.log('Result', indexes);

You can directly use filter without map function
const a = [
{ prop1: "abc", prop2: "yutu" },
{ prop1: "bnmb", prop2: "yutu" },
{ prop1: "zxvz", prop2: "qwrq" },
];
const res = a.filter((item) => {
return item.prop2==="yutu";
});
console.log(res);

Related

Merge Objects in a array and increase count

[{name:"abc",value:5},{name:"abc",value:10},{name:"abc1",value:5},{name:"abc1",value:15}]
I want to merge it by name so that the new array will be
[{name:"abc",value:15},{name:"abc1",value:20}]
Can i do it with es6 or a simple function
Using reduce and without find or findIndex
const data = [{name:"abc",value:5},{name:"abc",value:10},{name:"abc1",value:5},{name:"abc1",value:15}];
const summedDataObj = data.reduce((acc, entry) => {
if (acc[entry.name]) acc[entry.name].value += entry.value;
else acc[entry.name] = entry;
return acc;
}, {});
const summedDataArr = Object.values(summedDataObj);
console.log(summedDataArr);
We can do it via Array.reduce()
let data = [{name:"abc",value:5},{name:"abc",value:10},{name:"abc1",value:5},{name:"abc1",value:15}]
let result = data.reduce((a,{name,value}) => {
let obj = a.find(e => e.name === name)
if(obj){
obj.value += value
}else{
a.push({name,value})
}
return a
},[])
console.log(result)
you can group the data by name using this function:
function groupBy(arr, prop) {
const map = new Map(Array.from(arr, obj => [obj[prop], []]));
arr.forEach(obj => map.get(obj[prop]).push(obj));
return Array.from(map.values());
}
this yields this result:
[
[
{"name": "abc", "value": 5},
{"name": "abc", "value": 10}
],
[
{"name": "abc1", "value": 5},
{"name": "abc1", "value": 15}
]
]
which can be aggregated by using reduce on each resulting array:
groupedData.map(entry=>entry.reduce((acc,cur)=>({
...acc,
value: acc.value + cur.value
})))
so all together we get:
function groupBy(arr, prop) {
const map = new Map(Array.from(arr, obj => [obj[prop], []]));
arr.forEach(obj => map.get(obj[prop]).push(obj));
return Array.from(map.values());
}
const data = [{name:"abc",value:5},{name:"abc",value:10},{name:"abc1",value:5},{name:"abc1",value:15}]
const aggregatedData = groupBy(data,"name")
.map(entry=>entry.reduce((acc,cur)=>({
...acc,
value:acc.value+cur.value
})))
const obj = [{name:"abc",value:5},{name:"abc",value:10},{name:"abc1",value:5},{name:"abc1",value:15}]
arr = obj.reduce((obj, item) => {
let find = obj.find(i => i.name === item.name && i.date === item.date);
let _d = {
...item
}
find ? (find.value += item.value ) : obj.push(_d);
return obj;
}, [])
console.log(arr);
const data = [{
name: "abc",
value: 5
},
{
name: "abc",
value: 10
},
{
name: "abc1",
value: 5
},
{
name: "abc1",
value: 15
},
];
const groupArr = data.reduce((r, a) => {
const idx = r.findIndex((el) => el.name === a.name);
idx === -1 ? r.push(a) : (r[idx].value += a.value);
return r;
}, []);
console.log(groupArr);
You can achieve this with the help of Array#reduce method.
Live Demo :
const arr = [{name:"abc",value:5},{name:"abc",value:10},{name:"abc1",value:5},{name:"abc1",value:15}];
const res = arr.reduce((obj, curr) => {
if (obj.hasOwnProperty(curr.name) && obj[curr.name].name === curr.name) {
obj[curr.name].value += curr.value
} else {
obj[curr.name] = curr;
}
return obj
}, {});
console.log(Object.values(res));

combining duplicate key's values in JavaScript array

I have an Array that contain some keys/values one of the values is an array I want combining the value of array from all recorded that have same key in my Array.
Below is an Simple Example to demonstrate, I am not able to construct its logic so seeking help in building a logic to it.
[{"somekey":"Some Value Pushed"},{"somekey":"Second Value"}]
I want Result Like,
[{"somekey":["Some Value Pushed","Second Value"]}]
The reduce() function of Array Object in JavaScript can merge any array into a single Object.
I wrote a single-line code to solve this problem.
I updated result with the array.
const arr = [{
somekey: "Some Value Pushed",
},
{
somekey2: "Second Value2",
},
{
somekey: "Some Value Pushed",
},
{
somekey2: "Second Value3",
},
{
somekey3: "",
},
{},
];
const ansObj = arr.reduce(
(prv, cur) => {
Object.entries(cur).forEach(([key, v]) => key in prv ? prv[key].push(v) : (prv[key] = [v]));
return prv;
}, {}
)
const ansArray = Object.entries(ansObj).map(([key, value])=>({[key]:value}));
console.log(ansArray);
You can try something like this:
var array = [{
name: "foo1",
value: "val1"
}, {
name: "foo1",
value: ["val2", "val3"]
}, {
name: "foo2",
value: "val4"
}];
var output = [];
array.forEach(function(item) {
var existing = output.filter(function(v, i) {
return v.name === item.name;
});
if (existing.length) {
var existingIndex = output.indexOf(existing[0]);
output[existingIndex].value = output[existingIndex].value.concat(item.value);
} else {
if (typeof item.value === 'string')
item.value = [item.value];
output.push(item);
}
});
Or, another option using Lodash
function mergeNames (arr) {
return _.chain(arr).groupBy('name').mapValues(function (v) {
return _.chain(v).pluck('value').flattenDeep();
}).value();
}
Maybe something like:
const data = [
{"somekey":"Some Value Pushed"},
{"somekey":"Second Value", "otherkey": 1},
{"otherkey": 2}
];
const merge_and_group = (obj1, obj2) =>
Object.entries(obj2).reduce(
(acc, [key, val]) => {
acc[key] ??= [];
acc[key].push(val);
return acc;
},
obj1
);
const res = data.reduce(merge_and_group, {});
console.log(res);
const arr = [{
"somekey": "Some Value Pushed"
}, {
"somekey2": "Second Value2"
}, {
"somekey": "Some Value Pushed"
}, {
"somekey2": "Second Value3"
}]
const newarr = {}
arr.forEach(obj => {
for (const [key, value] of Object.entries(obj)) {
if (newarr[key]) newarr[key].push(value)
else newarr[key] = [value]
}
})
console.log(newarr)
Array.prototype.reduce() is a possible option.
the reduce() method executes a reducer function which is provided as an input on each element of the array and returning a single output value.
const array = [{"somekey":"Some Value Pushed"},{"somekey":"Second Value"}];
const res = array.reduce((acc, el) => {
const [key, value] = Object.entries(el)[0];
(acc[key] || (acc[key] = [])).push(value);
return acc;
}, {});
console.log(res)
Assuming each element of your array is an object with a single key.
const array = [
{ somekey: "Some Value Pushed" },
{ somekey: "Second Value" },
{ foo: "bar" },
{ foo: "baz" },
{ somekey: "Third Value" },
];
const result = [];
array.forEach(el => {
let [key, value] = Object.entries(el)[0];
for (let el of result) if (key in el) {
el[key].push(value);
return;
}
result.push({ [key]: [value] });
});
console.dir(result);
If your array has only "somekey" as keys then you can use map method as following:
const array = [{"somekey":"Some Value Pushed"},{"somekey":"Second Value"}];
const valuesArray = array.map(obj => obj.somekey);
result = [{"somekey":valuesArray}];
console.log(result)
If your array has other keys along with "somekey" and you like to separate values corresponding to only "somekey" then try the following:
const array = [{"somekey":"Some Value Pushed"},{"somekey":"Second Value"}, {"otherkey":"other Value"}];
const filteredArray = array.filter((obj) => {
return "somekey" in obj
}, []);
const valuesArray = filteredArray.map(obj => obj.somekey);
result = [{"somekey":valuesArray}];
console.log(result)

Merging two objects with same keys override first array

I would like to merge an array with another array. The only catch is that each array is within an object.
Intuitively I tried {...arrObj, ...newArrObj} however this leads newArrObj overwriting items in the arrObj.
const array = ['an', 'array'];
const newArray = [, , 'new', 'ehrray'];
const obj = {
key: { ...array
}
};
const newObj = {
key: { ...newArray
}
};
const merged = { ...obj,
...newObj
};
console.log(merged);
I would expect merged to be:
{
"key": {
"0": "an",
"1": "array",
"2": "new",
"3": "ehrray"
}
}
but receive
{
"key": {
"2": "new",
"3": "ehrray"
}
}
This might be useful
const a0 = ['1', '2', undefined , undefined, '5', '6', '7'];
const a1 = [undefined, undefined, '3', '4'];
function merge(a, b) {
return a.map(function(v,i){ return v?v:b[i]});
}
console.log(a0 > a1?merge(a0, a1):merge(a1, a0));
I wanted to updated that I ended up going with a recursive merge to get the nested object containing an array merged.
const array = ['an', 'array'];
const newArray = [, , 'new', 'ehrray'];
const obj = {
key: { ...array
}
};
const newObj = {
key: { ...newArray
}
};
const merge = (obj1, obj2) => {
const recursiveMerge = (obj, entries) => {
for (const [key, value] of entries) {
if (typeof value === "object") {
obj[key] = obj[key] ? { ...obj[key]
} : {};
recursiveMerge(obj[key], Object.entries(value))
} else {
obj[key] = value;
}
}
return obj;
}
return recursiveMerge(obj1, Object.entries(obj2))
}
console.log(merge(obj, newObj));
The idea is that there are unset values with only a few set. eg. const newArray = new Array(4); newArray[2] = 'new';
{ value: null }, even { value: undefined } is not the same thing as { foo: 42 } with no value at all. That's the reason that in your example "an" and "array" are overwritten with the nulls from the newArray.
This particular example you can solve by swapping the order in which you add the arrays to the result, but as soon as both arrays contain null-values there is no way to do it with spread-syntax / Object.assign alone. You have to implement the behaviour:
const array = new Array('an', 'array', null, null, "and", "more", "from", "array");
const newArray = new Array(null, null, 'new', 'ehrray');
function merge(a, b) {
const result = [];
for (let i = 0; i < a.length || i < b.length; ++i) {
result[i] = b[i] == null ? a[i] : b[i];
}
return result;
}
console.log(merge(array, newArray));

Json Array compare with different length in javascript

Below code which I am using for creating the new array if the id is the same in arr1 and arr2. But doesn't work since arr1 and arr2 are different. array 1 has index and arr2 is without index. screenshot for your reference. Can someone help?
Note: ID in arr1 is the same as EmpId in arr2
for(let i=0; i<arr1.length; i++) {
merged.push({
...arr1[i],
...(arr2.find((itmInner) => itmInner.id === arr1[i].id))}
);
}
console.log(merged);
Array1 looks like this :
[{"Active":1,"Id":1},
{"Active":1,"Id":3},
{"Active":1,"Id":2}]
Array2 looks something like this:
Below is the sample code on how I am framing array 2:
renderElement(activity){
var arr2 = [] ;
for(var i = 0; i < activity.length; i++) {
obj = activity[i];
if(obj.Id == 28){
fetch(geturl)
.then(function (response) {
return response.json();
})
.then(function (data) {
res = data;
arr2.push(res)
})
}
else{
// Do nothing
}
}
return arr2
}
Calling Render method like below:
outputarray = currentComponent.renderElement(activity);
console.log('output', outputarray)
Expected Output:
[{"Active":1,"Id":1,"Param1": true},
{"Active":1,"Id":3}, / Keep it as such if nothing exists in other array
{"Active":1,"Id":2, "Param2": false}]
You can try this approach instead:
Example #1
const arr1 = [
{ "Active":1, "Id":1 },
{ "Active":1, "Id":3 },
{ "Active":1, "Id":2 }
];
const arr2 = [
{
0: [
{
EmpId1: 1, Param1: true
}
]
},
{
1: [
{
EmpId2: 2,Param2: false
}
]
},
{
2: [
{
EmpId3: 2
}
]
},
];
const response = arr1
.reduce((acc, value) => {
const secondaryData = arr2.map((val, index) => {
const { [`EmpId${index + 1}`]: Id, ...others } = val[Object.keys(val)][0];
return { Id, ...others };
});
const match = secondaryData.findIndex(({ Id }) => Id === value.Id);
if (match >= 0) acc.push({...value, ...secondaryData[match]})
else acc.push(value);
return acc;
}, []);
console.log(response);
Example #2
const arr1 = [
{ "Active":1, "Id":1 },
{ "Active":1, "Id":3 },
{ "Active":1, "Id":2 }
];
const arr2 = [
[
{
EmpId1: 1,
Param1: true
}
],
[
{
EmpId2: 2,
Param2: false
}
],
[
{
EmpId3: 2
}
],
]
const response = arr1
.reduce((acc, value) => {
const secondaryData = arr2.map(([val], index) => {
const { [`EmpId${index + 1}`]: Id, ...others } = val;
return { Id, ...others };
});
const match = secondaryData.findIndex(({ Id }) => Id === value.Id);
if (match >= 0) acc.push({...value, ...secondaryData[match]})
else acc.push(value);
return acc;
}, []);
console.log(response);
Basically you can create a hash map by a object property and join on that property all the arrays, i.e. reduce an array of arrays into a result object, then convert the object's values back to an array. Since each array is reduced this means each array is only traversed once O(n) and the map object provides constant time O(1) lookup to match objects. This keeps the solution closer to O(n) rather than other solutions with a nested O(n) findIndex search, which yields a solution closer to O(n^2).
const mergeByField = (...arrays) => {
return Object.values(
arrays.reduce(
(result, { data, field }) => ({
...data.flat().reduce(
(obj, el) => ({
...obj,
[el[field]]: {
...obj[el[field]],
...el
}
}),
result
)
}),
{}
)
);
};
Load each array into a payload object that specifies the field key to match on. This will return all fields used to match by, but these can safely be ignored later, or removed, whatever you need. Example:
mergeByField(
{ data: arr1, field: "Id" },
{ data: arr2, field: "EmpId" },
);
const arr1 = [
{
Active: 1,
Id: 1
},
{
Active: 1,
Id: 2
},
{
Active: 1,
Id: 3
}
];
const arr2 = [[{ EmpId: 1, Param1: true }], [{ EmpId: 3, Param2: false }]];
const mergeByField = (...arrays) => {
return Object.values(
arrays.reduce(
(result, { data, field }) => ({
...data.flat().reduce(
(obj, el) => ({
...obj,
[el[field]]: {
...obj[el[field]],
...el
}
}),
result
)
}),
{}
)
);
};
console.log(
mergeByField({ data: arr1, field: "Id" }, { data: arr2, field: "EmpId" })
);

Is there a better way to replace my double forEach method?

I have two list, as bellow:
var a = ["a", "b"]
var b = [{name:"a1", belong_type:"a" }, {name:"a2", belong_type:"a" }, {name:"b1", belong_type:"b" },]
I want to put them like this:
var data = {}
a.forEach(a_item => {
data[a_item] = []
b.forEach(b_item => {
if (a_item === b_item.belong_type){
data[a_item].push(b_item)
}
})
})
console.log(data)
the result is :
{ a:
[ { name: 'a1', belong_task_type: 'a' },
{ name: 'a2', belong_task_type: 'a' } ],
b: [ { name: 'b1', belong_task_type: 'b' } ] }
I think my method use two forEach, I don't know whether there is a better way to realize the result, who can tell me if there is a better way?
You could use reduce method on a array and inside use filter method on b array to return objects where belong_type is equal to current element in reduce.
var a = ["a", "b"]
var b = [{name:"a1", belong_type:"a" }, {name:"a2", belong_type:"a" }, {name:"b1", belong_type:"b" }]
const result = a.reduce((r, e) => {
r[e] = b.filter(({belong_type}) => belong_type == e)
return r;
}, {})
console.log(result)
You could also use Object.assign method inside reduce to write it as a one-liner.
var a = ["a", "b"]
var b = [{name:"a1", belong_type:"a" }, {name:"a2", belong_type:"a" }, {name:"b1", belong_type:"b" }]
const result = a.reduce((r, e) => Object.assign(r, {[e]: b.filter(({belong_type}) => belong_type == e)}), {})
console.log(result)

Categories