1

Say I have a javascript objects that looks like this:

var events = 
[
    {
        name: "Foo event",
        submissions: [
            {
                id:"sub1",
                name:"First submission"
            },
            {
                id:"sub2",
                name:"Second submission"
            }
        ]
    },
    {
        name: "Bar event",
        submissions: [
            {
                id:"sub3",
                name:"First submission in bar"
            },
            {
                id:"sub4",
                name:"Second submission in bar"
            }
        ]
    }
]

I want to be able to write a lamba function that, for instance returns the entire object

{
    id:"sub4",
    name:"Second submission in bar"
}

Given the id sub4. Something like var submission = events.magicLamba(...) I tried using Array.prototype.find, but that works if I have to select an object from an array of objects, in this case there's one more search above that.

Thanks for the help.

EDIT: As many of you have pointed out, events wasn't valid. It was indeed an array, I must have miswritten it while trying to write a simpler example of my original data.

3
  • 1
    Your data object is invalid Commented Feb 12, 2017 at 15:08
  • Is events an array or object? Commented Feb 12, 2017 at 15:11
  • @ibrahimmahrir it's an array, I edited the question accordingly Commented Feb 13, 2017 at 1:55

5 Answers 5

2

First, your "events" aren't valid. Looks like it should just be an array, in which case you should just remove the outer most { }.

Since you used 'id' in your example, I assume that each submission id is unique.

While you could use .foreach(), there is no way to break out of a foreach, so you'll end up evaluating every submission -- even once you found the one you want.

Accordingly, I'd use for loops and break out once I found what I was looking for. This will be more efficient. I added some console logging so you can see that once you find what you want, you won't keep evaluating data.

    var events = [
        {
            name: "Foo event",
            submissions: [
                { id: "sub1", name: "First submission"},
                { id: "sub2", name: "Second submission"}
            ]
        },
        {
            name: "Bar event",
            submissions: [
                { id: "sub3", name: "First submission in bar" },
                { id: "sub4", name: "Second submission in bar"}
            ]
        }
    ];

    function findSubmission(events, submissionId) {
        var result = null;
        for (var event of events) {
            console.log("new event evaluated");
            for (var submission of event.submissions) {
                console.log("evaluating submission: ",submission.id);
                if (submission.id === submissionId) {
                    result = submission;
                    break;
                }
            }
            if (result) break;
        }
        return result;
    };

    var matchingSubmission = findSubmission(events, "sub3");
Sign up to request clarification or add additional context in comments.

1 Comment

This is the most efficient answer so far, I was hoping there would be a way to find the matching submission using lambda only (and without evaluating every single submission, as Meier and wedeb answer unfortunately do).
1

Well, like I've commented your data seems to be invalid

events = { [] } // is not correct syntax.
Maybe it should be events = []

Ok lets imagine it is an array, that would make sense at least.

You findSubmission could look like this:

function findSubmission(events, submissionId) {
  var result;
  events.forEach(function(event) {
    event.submissions.forEach(function(submission) {
      if (submission.id === submissionId) {
        result = submission;
      }
    })
  });

  return result;
}

submission = findSubmission(events, "sub4");

2 Comments

Doesn't this keeps searching for result even if it finds it? Therefor eimpacting (even if slightly) performance=
true, this is a simple piece of code, could be optimized
1

You can use some to achieve what you want like this:

var events = [{name: "Foo event",submissions: [{id:"sub1",name:"First submission"},{id:"sub2",name:"Second submission"}]},{name: "Bar event",submissions: [{id:"sub3",name:"First submission in bar"},{id:"sub4",name:"Second submission in bar"}]}]

function find(arr, id) {
  var res;                          // uninitialized thus the result will be undefined if nothing is found
  arr.some(                         // unlike forEach some will stop after the first match (after the first callback call that returned true)
    o => o.submissions.some(
      o => o.id == id && (res = o)  // return true if o.id == id, false otherwise (and if o.id == id then assign o to result)
    ) 
  );
  return res;
}

console.log(find(events, "sub2"));
console.log(find(events, "sub4"));
console.log(find(events, "sub499999"));

8 Comments

I like this a lot. It does have the overhead of passing all the events and then passing all the submissions (instea of passing all the submissions once) but it looks very clean
@FilippoVigani Just note that the submission objects are not copied! But just a reference to them is stored in the result array of reduce. So the result array of reduce is as optimized as an array of numbers!
That's the beauty of javascript objects!
While the code in this solution is brief (and does 'look' nice), in my opinion it is a deceptively dangerous approach, as it is multiple ORDERS OF MAGNITUDE less efficient than the previous answers using .for(), .foreach(), and even .map(). Demonstrated here.
@RobM I added another approach of searching using some, now it's as fast as it could be!
|
0

You correctly noticed that find() only helps you to get a submission from an submissions-array. You can use another nested loop with map(), which will return all the results of the nested find() calls. As last step you need remove all the undefined values from the result array.

function findSubmissionUsingMap(events, submissionId) {
  return events.map( function(event) {
    return event.submissions.find( function(submission) {
      return (submission.id === submissionId)
    })
  }).filter( function(x) {return x});
};

6 Comments

Thanks you for the feedback. Of course there are several ways to do it. I tried to give an answer using the array methods.
While .map() is very useful, in my opinion it isn't appropriate here because there is no need to map results, you are simply using it to do looping, which can be done and is more readable with .for() or .foreach(). The result of this approach is a less efficient code and bunch of unnecessary actions, including the inability to stop processing and the need to do extra filtering.
@RobM using your fiddle I tried out my own answer to my question, and it works wonders, but I can't figure out WHY it does. Am I missing something? I would appreciate it a lot if you were to look into it.
@FilippoVigani, glad to hear you like the results. The answer I posted works pretty well primarily because of two things: 1) It doesn't need to process every record and stops when it finds the one it wants (which, as mentioned is ok as long as your ids are unique). 2) It is that it is only reading values and not making unnecessary writes as part of the repeating work.
@RobM sure, but why does It perform better than the solution using the two for cycles?
|
0

I found an interesting way that works really well for me. It only uses lambda functions (namely some and find) and seems pretty efficient.

function findSubmissionUsingSome(events, submissionId){
    var submission;
    events.some(event => {
        submission = event.submissions.find(s => s.id === submissionId); //search for the submission in the current event, and store it in a variable
        return submission; //returns truthy value if submission is found and exits some
    });
    return submission;
}

You can see the test run here, I'm not sure why it performs so well, and would like some clarifications (maybe it works in parallel?). Many thanks to RobM for the test code.

4 Comments

Yeah you're right! find inside some is even better!
@FilippoVigani, actually this is less efficient than my answer that you originally accepted (and now unaccepted?) See this fiddle. You must consider that the demo code has a timing granularity of ES dates, which is down one millisecond. That was enough to demonstrate against the other answers. At this point, bigger data and/or larger sample sizes are required (and in this fiddle). I brought the 'big' size down, so it will run on more devices. On my iPad the for loop solution is generally 300%-400% faster. On my top of the line notebook 40% to 110% faster.
@FilippoVigani Please compare for yourself and report your results.
@RobM it does look faster now. However I accepted this answer as the question asked for a solution using lambda functions, and has an acceptable performance

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.