Related
I have Two Array with nested objects
let arrOne = [{ id:01, name:'haris' },{ id:02, name:'papi' },{ id:03, name:'john' }];
let arrTwo = [{ jobId:03, name:'haha' },{ jobId:01, name:'kaka' }];
Now I want to filter out the arrOne in such a way that arrOne id is matched with arrTwo jobId Like this:
arrOne.filter((ele,index) => ele.id == (arrTwo)[index]?.jobId)
But it returns [ ] empty array, although if I will rearrange arrTwo in such way like:
let arrTwo = [{ jobId:01, name:'haha' },{ jobId:02, name:'kaka' }];
it will return the matched value.
so what's the problem here & How to resolve this?
Thank you
A simple solution using find() inside filter() to get the result
And for your question,it's because you are using index,however the value with same index are not the same in these two array. And if you change the element order in arrTwo,then the same index will get some data for you
let arrOne = [{ id:01, name:'haris' },{ id:02, name:'papi' },{ id:03, name:'john' }];
let arrTwo = [{ jobId:03, name:'haha' },{ jobId:01, name:'kaka' }];
let result = arrOne.filter(a1 => arrTwo.find(a2 => a2.jobId == a1.id));
// or with some()
//let result = arrOne.filter(a1 => arrTwo.some(a2 => a2.jobId == a1.id));
console.log(result);
I have a JSON response as below:
[{
"id": 1,
"food": {
"fruits": ["Banana", "Orange", "Apple", "Mango"],
"veggies": ["greens", "peppers", "carrot", "potatoes"],
}
},
{
"id": 2,
"food": {
"fruits": ["grapes", "berries", "peach", "pears"],
"veggies": ["cabbage", "spinach"],
"dairy": ["nutmilk", "goatmilk"]
}
}
]
Now i want to merge the Arrays each "id" (1,2 in example) into string ( ; delimited) like below:
id_1 = Banana;Orange;Apple;Mango;greens;peppers;carrot;potatoes
// observer "id_2" has additional array - "dairy"
id_2 = grapes;berries;peach;pears;cabbage;spinach;nutmilk;goatmilk
The key's are dynamic so for some records there are 2 arrays and for some records it can be 3 or 4 and may be 1.
I tried using react/Java Script Array.concat(), but i am not sure how to do it dynamically. Please help me. Thank you.
This is doable easily using Object.values().flat().join(';') demonstrated below:
let arr=[{"id":1,"food":{"fruits":["Banana","Orange","Apple","Mango"],"veggies":["greens","peppers","carrot","potatoes"],}},{"id":2,"food":{"fruits":["grapes","berries","peach","pears"],"veggies":["cabbage","spinach"],"dairy":["nutmilk","goatmilk"]}}];
const result = arr.map(({id,food}) => ({id, food: Object.values(food).flat().join(';')}));
console.log(result);
You may easily restructure the output by simply changing it to e.g. ["id_"+id]: Object.values(...)
First flatten using map, flat and join. Then convert the resulting array of objects to a single object using assign.
var db = [{"id": 1,"food": {"fruits": ["Banana", "Orange", "Apple", "Mango"], "veggies": ["greens","peppers","carrot","potatoes"], }},{"id" : 2,"food": {"fruits": ["grapes", "berries", "peach", "pears" ], "veggies": ["cabbage","spinach"], "dairy": ["nutmilk","goatmilk"]}}];
var flat = db.map(
({id, food}) => ({[`id_${id}`]: Object.values(food).flat().join(';')})
);
var result = Object.assign(...flat);
console.log(result);
This is really two problems: looping through an array of objects to combine them into one object, and looping through an object to concat all of its array.
Tackling the second one first, something like this would work:
const concatArraysInObject = obj =>
Object.values(obj).reduce((result, arr) => result.concat(arr), []);
const input = { a: [1,2,3], b: [4,5,6], c: [7,8,9] };
const output = concatArraysInObject(input);
console.log(output);
Object.values() will give you an array of all arrays in an object.
The reduce() function takes a two parameters: a function and initial value.
The function should also take (at least) 2 parameters: the result of the last call (or initial value) and the current value in the array.
It'll call the function once for each element in the array.
Now, with that solved, we can tackle the first problem.
For this, we can also use reduce() as well, and we'll construct our combined object on each call.
const concatArraysInObject = (obj) =>
Object.values(obj).reduce((result, arr) => result.concat(arr), []);
const mergeObjectsInArray = (arr, dataKey) =>
arr.reduce((result, obj) => ({ ...result, [obj.id]: concatArraysInObject(obj[dataKey]) }), {});
const input = [
{ id: 'A', data: { a: [1,2,3], b: [4,5,6] } },
{ id: 'B', data: { c: [7,8,9], d: [10,11,12] } }
];
const output = mergeObjectsInArray(input, 'data');
console.log(output);
An important note of warning: object key order is NOT guaranteed in JavaScript. While 99% of the time they will be in the order you expect, this is not a guarantee, so if you depend on the order of the keys for the order of the array (if order matters), you'll want to change your input structure. If order doesn't matter, it is probably fine how it is.
Try this using basic for loop. Inside you will compute key dynamically and value being flattened array of Object.values of the iterating object.
var input = [{
id: 1,
food: {
fruits: ["Banana", "Orange", "Apple", "Mango"],
veggies: ["greens", "peppers", "carrot", "potatoes"]
}
},
{
id: 2,
food: {
fruits: ["grapes", "berries", "peach", "pears"],
veggies: ["cabbage", "spinach"],
dairy: ["nutmilk", "goatmilk"]
}
}
];
var temp = [];
for (var i = 0; i < input.length; i++) {
temp.push({
[`id_${input[i].id}`]: Object.values(input[i].food)
.flat(1)
.join(";")
});
}
console.log(temp); // this gives you an array
console.log(Object.assign(...temp));// in case you require one single object
I have two arrays of objects:
[
0: {key1: value1, key2: value2, key3: value3},
1: {key1: value1, key2: value2, key3: value3}
]
[
0: {stop_id: 173, file_id: "1", key_type: null, key_value: "0020", seg_beg: 32},
1: {stop_id: 176, file_id: "1", key_type: null, key_value: "0201", seg_beg: 10},
2: {stop_id: 176, file_id: "1", key_type: null, key_value: "0201", seg_beg: 10}
]
I need to check to see if the values of any of the keys in the first object, match any of the values of the key_value...keys, in the second object, and then set a variable further up to the stop_id value in the matched record. Like this:
if(object1.value === object2.key_value){
match = object2[iterator].stop_id;
}
To simplify this, I have attempted to just grab the values of the first object:
//pd.segs is object 1
let pdSegValues = [];
for(let i=0;i<pd.segs.length;i++){
pdSegValues.push(Object.values(pd.segs[i]));
}
But that gets me an array of arrays again, and basically puts me back in the same situation. I'm suffering from a fried brain, and admittedly have a weakness for loops. Can anyone show me a decent way to accomplish what I need here?
You can do this by collecting the values you want to test and then using some.
let arr1 = [
{"a1": "value1", "b1": "value2"},
{"a2": "0020", "b2": "value22"},
{"a3": "value111", "b3": "0201"}
];
let arr2 = [
{stop_id: 173, file_id: "1", key_type: null, key_value: "0020", seg_beg: 32},
{stop_id: 176, file_id: "1", key_type: null, key_value: "0201", seg_beg: 10},
{stop_id: 176, file_id: "1", key_type: null, key_value: "0201", seg_beg: 10}
];
// accumulate unique arr1 values to an array
let arr1Values = Array.from(arr1.reduce((acc, curr) => {
Object.values(curr).forEach(v => acc.add(v));
return acc;
}, new Set()));
// accumulate all unique arr2 "key_value"
let arr2KeyValues = arr2.reduce((acc, curr) => {
acc.add(curr.key_value);
return acc;
}, new Set());
console.log(arr1Values);
console.log(Array.from(arr2KeyValues));
// Test if any of the values in objects in the first array are
// equal to any of the key_values in the second array
console.log(arr1Values.some(k => arr2KeyValues.has(k)));
It looks like you're going to have to compare every object in one array to every object's keys in another array. An initial brute force approach has 3 nested for loops:
// Loop through the objects in the first array
for (const objectA of arrayA) {
// Loop through that object's keys
for (const key in objectA) {
// Loop through the objects in the second array
for (const objectB of arrayB) {
if (objectA[key] === objectB.key_value) {
// do all the stuff
}
}
}
}
Here's what I ended up doing, just to leave a record :)
let stopRules = pd.stopRules;
let pdSegs = pd.segs;
let routeStopsTest = [];
//Returns a flat array of all unique values in first object
//Thanks #slider!
let pdSegValues = Array.from(pdSegs.reduce((acc, curr) => {
Object.values(curr).forEach(v => acc.add(v));
return acc;
}, new Set()));
//Pushes all objects from stopRules array to a holding array
//When they match individual segments in the pdSegs array
pdSegValues.forEach( seg => {
let nullTest = stopRules.filter(o => o.key_value === seg);
if(nullTest.length !== 0){
routeStopsTest.push(nullTest);
}else{}
});
Then all I have to do is flatten the resulting array of objects, and I have the results I need, which I can then loop through for the original purpose.
Thank you, everyone, for the insightful input. I've learned a fair bit here :)
If I have two arrays like
array1 = [{id: 'id1'}, {id:'id2'}, {id:'id3'}, {id:'id3'}]
array2 = [{id: 'id1'}, {id:'id2'}, {id:'id3'}]
The expected result should be [{id:'id3'}]
My method works perfectly for distinct arrays.
idsFrom1 = _.pluck(array1, 'id');
idsFrom2 = _.pluck(array2, 'id');
var diff = _.difference(_.union(idsFrom1, idsFrom2), _.intersection(idsFrom1, idsFrom2))
I think your issue is that you have to use some mechanism to count the deleted elements, such as _.countBy:
var first = [{
id: 'id1'
}, {
id: 'id2'
}, {
id: 'id3'
}, {
id: 'id3'
}]
var second = [{
id: 'id1'
}, {
id: 'id2'
}, {
id: 'id3'
}]
function unwrap (o) {
return o[this]
}
function wrap (k) {
return _.object([k], [String(this)])
}
function counted(array) {
return _.countBy(array, unwrap, 'id')
}
function constant() {
return String(this)
}
function difference(first, second) {
return _.flatten(_.values(_.mapObject(counted(first), function(v, k) {
return _.range(0, v - this[k]).map(constant, k)
}, counted(second)))).map(wrap, 'id')
}
console.log(difference(first, second))
<script src="http://underscorejs.org/underscore-min.js"></script>
This one doesn't use splice() and instead it "marks" each array member that should be removed by setting it to null and then uses compact() to take them all out at once. The speed of the indexOf() actually improves as idsFrom2 gets smaller with splice, so the faster approach depends on the number of matches and sizes of arrays.
array1 = [{id: 'id1'}, {id:'id2'}, {id:'id3'}, {id:'id3'}]
array2 = [{id: 'id1'}, {id:'id2'}, {id:'id3'}]
idsFrom1 = _.pluck(array1, 'id');
idsFrom2 = _.pluck(array2, 'id');
for(var i=0;i<idsFrom1.length;i++) {
var index = idsFrom2.indexOf(idsFrom1[i]);
if(index > -1) {
idsFrom1[i] = null;
idsFrom2[index] = null;
}
}
diff = [].concat(_.compact(idsFrom1),_.compact(idsFrom2));
objarr = [];
for(var i=0;i<diff.length;i++) objarr.push({"id":diff[i]});
console.log(objarr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
This approach steps through one of the arrays and removes elements from both arrays that match as it moves through the array. That way anything without a partner in both arrays will remain at the end:
var array1 = [{id: 'id1'}, {id:'id2'}, {id:'id3'}, {id:'id3'}]
var array2 = [{id: 'id1'}, {id:'id2'}, {id:'id3'}]
idsFrom1 = _.pluck(array1, 'id');
idsFrom2 = _.pluck(array2, 'id');
for(var i=0;i<idsFrom1.length;) {
var index = idsFrom2.indexOf(idsFrom1[i]);
if(index > -1) {
idsFrom1.splice(i,1);
idsFrom2.splice(index,1);
}
else i++;
}
diff = [].concat(idsFrom1,idsFrom2);
objarr = [];
for(var i=0;i<diff.length;i++) objarr.push({"id":diff[i]});
console.log(objarr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
Ok, this one appears to be the fastest in all of my tests. Use the splice() on the idsFrom2 array to get it to shrink as you move through idsFrom1, and set null on the idsFrom1 array matches and use compact() afterwards.
var array1 = [{id: 'id1'}, {id:'id2'}, {id:'id3'}, {id:'id3'}];
var array2 = [{id: 'id1'}, {id:'id2'}, {id:'id3'}];
idsFrom1 = _.pluck(array1, 'id');
idsFrom2 = _.pluck(array2, 'id');
for(var i=0;i<idsFrom1.length;i++) {
var index = idsFrom2.indexOf(idsFrom1[i]);
if(index > -1) {
idsFrom1[i] = null;
idsFrom2.splice(index,1);
}
}
diff = [].concat(_.compact(idsFrom1),idsFrom2);
objarr = [];
for(var i=0;i<diff.length;i++) objarr.push({"id":diff[i]});
console.log(objarr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
Try this. If you don't want to modify your original arrays, then you can create copies and use them for the modification
var array1 = [{
id: 'id1'
}, {
id: 'id2'
}, {
id: 'id3'
}, {
id: 'id3'
}],
array2 = [{
id: 'id1'
}, {
id: 'id2'
}, {
id: 'id3'
}];
_.each(array1, function(obj1, i) {
_.each(array2, function(obj2, j) {
// Make sure the objects are not cleared off by a previous comparison
var isEqual = array1[i] && array2[j] && _.isEqual(obj1, obj2);
if (isEqual) {
array1[i] = array2[j] = null;
}
});
});
console.log([].concat(_.compact(array1), _.compact(array2)));
<script src="http://underscorejs.org/underscore-min.js"></script>
I have an array with several category objects, each of which has an items property containing an array of item objects. I want to map each item in each category to an object[] with objects that have the properties value and label. For some reason, I can't perform the concatenation.
var categories = [{
name: "category1",
items: [{
itemId: 1,
name: "Item1"
}, {
itemId: 2,
name: "Item2"
}]
}, {
name: "category2",
items: [{
itemId: 3,
name: "Item3"
}, {
itemId: 4,
name: "Item4"
}]
}];
var items = [];
for(var i = 0; i < categories.length; i++){
items.concat($.map(categories[i].items,function(elem){
return {value:elem.itemId, label:elem.name};
}));
}
console.log(items); //prints []
Expected Result
[{
label: "Item1",
value: "1"
},
{
label: "Item2",
value: "2"
},{
label: "Item3",
value: "3"
},{
label: "Item4",
value: "4"
}
I feel as if I am missing something very basic. I logged the result of the $.map function and it appears to be returning an []. Can anyone figure out the issue?
JSFiddle: http://jsfiddle.net/vymJv/
Another method using straight Javascript:
var x = categories.map(function(val) {
return val.items;
}).reduce(function(pre, cur) {
return pre.concat(cur);
}).map(function(e,i) {
return {label:e.name,value:e.itemId};
});
Output: x = [{label: "Item1", value: 1}, {label: "Item2", value: 2}, …]
The concat() method is used to join two or more arrays.
This method does not change the existing arrays, but returns a new
array, containing the values of the joined arrays.
http://jsfiddle.net/vymJv/1/
for(var i = 0; i < categories.length; i++){
items = items.concat($.map(categories[i].items, function(elem) {
return {value: elem.itemId, label: elem.name};
}));
}
updated with flatMap(not compatible with IE)
categories.flatMap((categories) => categories.items)
flatMap() method returns a new array formed by applying a given callback function to each element of the array, and then flattening the result by one level.
const items = categories
.map(category => category.items)
.reduce((prev, current) => [...prev, ...current])
.map((e, i) => ({ label: e.name, value: e.itemId }));
We could extend the array prototype by creating concatAll which will concatenate all of your arrays by iterating over each subarray, dumping each value into a new array.
Array.prototype.concatAll = function() {
var results = [];
this.forEach(function(subArray) {
subArray.forEach(function(subArrayValue) {
results.push(subArrayValue);
});
});
return results;
};
Then, we can get the desired result with the following:
let items = categories.map(function(category) {
return category.items.map(function(item) {
return {label: item.name, value: item.itemId};
});
}).concatAll();
We get the items by translating each category into an array of items. Because we have two categories, we will end up with two arrays of items. By applying concatAll on the final result, we flatten those two arrays of items and get the desired output.
This piece of code solves your task using a functional programming approach:
var items = [].concat.apply([], categories.map(cat =>
cat.items.map(elem => ({ value:elem.itemId, label:elem.name })))
)
Explanation: Function.prototype.apply() has the syntax fun.apply(thisArg, [argsArray]) and lets us provide parameters to a function in an array. Array.prototype.concat() combines an arbitrary amount of parameters into one array. If we now write Array.prototype.concat.apply([], [category1Items, ..., categoryNItems]), it actually equals [].concat(category1Items, ..., categoryNItems), which concatenates all parameters together. You can also replace Array.prototype.concat by [].concat to keep it shorter. Otherwise we just use standard mapping to get the job done.
You could also split the code apart a bit more for clarity:
function getItem(elem){
return {value:elem.itemId, label:elem.name};
}
function getCategoryItems(cat) {
return cat.items.map(getItem);
}
function flatten(arr) {
return Array.prototype.concat.apply([], arr);
}
var items = flatten(categories.map(getCategoryItems));